본문 바로가기

Spring

[Spring] SecurityConfig, OAuth2UserDetailsService 순환 참조 오류

오류

Spring Security와 OAuth 2.0 프레임워크를 이용하여 소셜로그인을 구현하던 중 SecurityConfig 파일과 DefaultOAuth2UserService 클래스를 상속받은 OAuth2UserDetailsService 클래스 사이에서 순환 참조 오류가 발생했다.

오류 원인

SecurityConfig

@Configuration
@Log4j2
@EnableWebSecurity
@EnableGlobalMethodSecurity(securedEnabled = true)
@RequiredArgsConstructor
public class SecurityConfig {

    private final OAuth2UserDetailsService oAuth2UserDetailsService;

    private final JWTUtil jwtUtil;

    @Bean
    PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    ...

OAuth2UserDetailsService

@Log4j2
@Service
@RequiredArgsConstructor
public class OAuth2UserDetailsService extends DefaultOAuth2UserService {

    private final MemberRepository memberRepository;
    
    private final PasswordEncoder passwordEncoder;

    ...

    private Member saveSocialMember(String email) {

        Optional<Member> result = memberRepository.findByEmail(email);

        if (result.isPresent()) {
            return result.get();
        }

        String password = getRandomPassword(12);

        Member member = Member.builder()
                .email(email)
                .name(email)
                .password(passwordEncoder.encode(password))
                .fromSocial(true)
                .build();

        member.addMemberRole(MemberRole.USER);

        memberRepository.save(member);

        return member;
    }

    ...
}

코드 리뷰를 해보니 SecurityConfig 클래스에서 참조한 OAuth2UserDetailsService 클래스에서 다시 SecurityConfig 클래스에서 빈으로 등록한 PasswordEncoder 인터페이스를 참조하면서 생긴 에러였다.

해결 방법

OAuth2UserDetailsService

@Log4j2
@Service
@RequiredArgsConstructor
public class OAuth2UserDetailsService extends DefaultOAuth2UserService {

    private final MemberRepository memberRepository;

    ...

    private Member saveSocialMember(String email) {

        Optional<Member> result = memberRepository.findByEmail(email);

        if (result.isPresent()) {
            return result.get();
        }

        String password = getRandomPassword(12);

        Member member = Member.builder()
                .email(email)
                .name(email)
                .password(new BCryptPasswordEncoder().encode(password))
                .fromSocial(true)
                .build();

        member.addMemberRole(MemberRole.USER);

        memberRepository.save(member);

        return member;
    }

    ...
}

OAuth2UserDetailsService 클래스에서 passwordEncoder 객체를 사용한 곳이 한 곳 밖에 없어서 PasswordEncoder 인터페이스를 의존성 주입하지 않고 BCryptPasswordEncoder() 클래스를 새로 생성해서 문제를 해결했다.