본문 바로가기
Spring

[Spring][Error] Spring Security 적용할 때 circular reference, dependency가 cycle 형성하는 것 해결하기

by wadekang 2022. 3. 14.

 

SecurityConfig
@RequiredArgsConstructor
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    ...
    
    private final UserService userService;

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

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userService).passwordEncoder(passwordEncoder());
    }

    ...
}

 

UserService
@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class UserService implements UserDetailsService {
	
    ...
    
    private final UserRepository userRepository;
    private final BCryptPasswordEncoder passwordEncoder;

    @Override
    public UserDetails loadUserByUsername(String loginId) throws UsernameNotFoundException {
        User user = userRepository.findByLoginId(loginId)
                .orElseThrow(() -> new UsernameNotFoundException("Failed: No User Info"));

        return user;
    }
    
    ...
}

 

Spring Security를 적용하면서 SecurityConfig와 UserService를 위와 같이 구현했는데 아래와 같은 에러가 발생했다.

The dependencies of some of the beans in the application context form a cycle:

┌─────┐
|  securityConfig defined in file [\SecurityConfig.class]
↑     ↓
|  userService defined in file [\UserService.class]
└─────┘


Action:

Relying upon circular references is discouraged and they are prohibited by default. 
Update your application to remove the dependency cycle between beans. 
As a last resort, it may be possible to break the cycle automatically 
by setting spring.main.allow-circular-references to true.


Process finished with exit code 1

이 에러는 SecurityConfig의 configure method에서 UserService를 참조하고 있고, UserService에서 BCryptPasswordEncoder를 사용하기 위해 다시 SecurityConfig를 참조하기 때문에 참조 사이클이 생겨 에러가 발생한 것이다.

 

에러를 해결하기 위해 UserDetailsService를 구현하는 Service를 따로 하나 구현하여 SecurityConfig에서 UserService가 아닌 해당 Service를 참조하게 하면 해결된다. 에러를 해결하기 위해 아래와 같이 구현했다.

 

UserDetailsServiceImpl
@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class UserDetailsServiceImpl implements UserDetailsService {

    private final UserRepository userRepository;

    @Override
    public UserDetails loadUserByUsername(String loginId) throws UsernameNotFoundException {
        User user = userRepository.findByLoginId(loginId)
                .orElseThrow(() -> new UsernameNotFoundException("Failed: No User Info"));

        return user;
    }
}

 

SecurityConfig
@RequiredArgsConstructor
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    ...
    
    private final UserDetailsServiceImpl userDetailsService;

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

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
    }

    ...
}

configure method에서 이제 UserDetailsServiceImpl을 참조한다.

 

UserService
@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class UserService {
	
    ...
    
    private final UserRepository userRepository;
    private final BCryptPasswordEncoder passwordEncoder;
    
    ...
}

UserService는 위와 같이 implements UserDetailsService를 지우고 loadUserByUsername method는 위의 UserDetailsServiceImpl로 옮겨주었다. 

 

 

 

 

댓글