WindsomeProject - 스프링 순환 참조 문제

박민수·2023년 12월 21일
0

WindsomeProject

목록 보기
4/32
post-thumbnail

스프링 순환 참조

AccountService에서 AuthenticationManager를 의존성 주입 후 아래와 같은 순환 참조(circular reference)가 발생했다.
확인해 보니 AccountService도 AuthenticationManager를 참조하고 있고, Authentication도 AccountService를 참조하고 있어서 스프링에서 어떤 스프링 빈을 먼저 만들어야 할지 결정하지 못하게 되어 순환 참조가 발생한 것이다.

SpringSecurity

  • SpringSecurity에서 AccountService(RememberMe 설정에서 UserDetailsService 등록 필요) 참조 중
@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class SecurityConfig {
	private final AccountService accountService;
    
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
                .rememberMe()
                .userDetailsService(accountService)
                .tokenRepository(getJDBCRepository());
		...
    }
}   

AccountService

  • AccountService에서 SecurityConfig(AuthenticationManager) 참조 중
  • AccountService에서 UserDetailsService 구현(SpringSecurity에서 참조 중)
@Service
@RequiredArgsConstructor
@Slf4j
@Transactional
public class AccountService implements UserDetailsService {

    private final AccountRepository accountRepository;
    private final PasswordEncoder passwordEncoder;
    private final ModelMapper modelMapper;
    private final EmailService emailService;
    private final AuthenticationManager authenticationManager;

    @Transactional(readOnly = true)
    @Override
    public UserDetails loadUserByUsername(String userIdOrEmail) throws UsernameNotFoundException {
        // id로 조회
        Account account = accountRepository.findByUserIdentifier(userIdOrEmail);
        if (account == null) {
            // email로 조회
            account = accountRepository.findByEmail(userIdOrEmail);
        }

        if (account == null) {
            throw new UsernameNotFoundException(userIdOrEmail);
        }

        return new UserAccount(account);
    }
}

해결책

AccountService에서 UserDetailsService를 구현하지 않고, 별도의 CustomUserDetailsService를 만들어 이곳에서 UserDetailsService를 구현하도록 하였다.
따라서 SecurityConfig는 더 이상 AccountService를 참조하지 않고, CustomUserDetailsService를 참조하게 됐다.

CustomUserDetailsService

  • AccountService에서 UserDetailsService를 구현하지 않고, 별도의 CustomUserDetailsService를 만들어 이곳에서 UserDetailsService를 구현하도록 변경했다.
@Component
@RequiredArgsConstructor
public class CustomUserDetails implements UserDetailsService {

    private final AccountRepository accountRepository;
    
    @Override
    public UserDetails loadUserByUsername(String userIdOrEmail) throws UsernameNotFoundException {
        // id로 조회
        Account account = accountRepository.findByUserIdentifier(userIdOrEmail);
        if (account == null) {
            // email로 조회
            account = accountRepository.findByEmail(userIdOrEmail);
        }

        if (account == null) {
            throw new UsernameNotFoundException(userIdOrEmail);
        }

        return new UserAccount(account);
    }
}

SecurityConfig

  • SecurityConfig에서 더 이상 AccountService를 참조 하지 않는다.
@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class SecurityConfig {
	private final CustomUserDetails customUserDetails;
    
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
                .rememberMe()
                .userDetailsService(customUserDetails)
                .tokenRepository(getJDBCRepository());
		...
    }
}
profile
안녕하세요 백엔드 개발자입니다.

0개의 댓글

관련 채용 정보