AccountService에서 AuthenticationManager를 의존성 주입 후 아래와 같은 순환 참조(circular reference)가 발생했다.
확인해 보니 AccountService도 AuthenticationManager를 참조하고 있고, Authentication도 AccountService를 참조하고 있어서 스프링에서 어떤 스프링 빈을 먼저 만들어야 할지 결정하지 못하게 되어 순환 참조가 발생한 것이다.
@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class SecurityConfig {
private final AccountService accountService;
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.rememberMe()
.userDetailsService(accountService)
.tokenRepository(getJDBCRepository());
...
}
}
@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를 참조하게 됐다.
@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);
}
}
@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class SecurityConfig {
private final CustomUserDetails customUserDetails;
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.rememberMe()
.userDetailsService(customUserDetails)
.tokenRepository(getJDBCRepository());
...
}
}