┌─────┐
| webSecurityConfigure
↑ ↓
| org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration$EnableWebMvcConfiguration
↑ ↓
| org.springframework.security.config.annotation.web.configuration.OAuth2ClientConfiguration$OAuth2ClientWebMvcSecurityConfiguration
└─────┘
위와 같은 오류가 발생했다.
Spring 2.6이후부터는 순환참조가 되지 않기 때문에 yml파일에서 순환참조를 허락하도록 바꿔줘야 한다.
임시 해결책 - Bob
OAuth 관련 커스텀 객체들을 Bean이 아니라 직접 생성하도록 합니다.
예상 원인
SpringSecurity가 초기화 될 때 기본적인 OAuth 설정을 구성하게 되는데, 이 때 OAuth 관련 Bean들을 등록하는 로직도 실행되는 것 같습니다. 하지만 저희가 직접 관련 객체들을 @Bean 선언을 할 경우, 기본적으로 생성하도록 예약되어 있던 Bean들을 저희가 가로채서 초기화 흐름을 바꾸게 되어 순환 참조가 일어나는 것 같아요. 특히 OAuth Bean들은 의존 관계가 복잡해서 이런 현상이 발생하는 것 같습니다.
그래서 맨 아래에 작성된 저희만의 OAuth 설정 코드가 있는 Configure 메서드가 실행되기도 전에 스프링이 종료됩니다. 즉 설정 코드 문제는 아닙니다.
그런데 저희는 SpringSecurity가 만든 OAuth 객체와 설정들을 그대로 사용하지 않을 것이므로,
- 일단, ApplicationContext 초기화 과정에서 SpringSecurity가 자동으로 bean들을 등록하도록 만듭니다.
- 커스텀 객체들을 Bean이 아닌 수동으로 등록하도록 코드를 변경합니다. 이 중 커스텀 객체들이 의존하는 Bean들은 Autowired가 아닌 applicationContext에서 빼오는 방식으로 변경합니다. 이는 아래의 객체 생성 메서드들이 스프링의 Bean 초기화가 전부 끝난 시점에 실행되기 때문에 가능합니다.
WebSecurityConfigure의 OAuth 전용 객체들
(@Bean 제거)@Configuration @EnableWebSecurity @Slf4j public class WebSecurityConfigure extends WebSecurityConfigurerAdapter { public JwtAuthenticationFilter jwtAuthenticationFilter() { return new JwtAuthenticationFilter(jwtConfigure.getHeader(), jwt()); } public AuthorizationRequestRepository<OAuth2AuthorizationRequest> authorizationRequestRepository() { return new HttpCookieOAuth2AuthorizationRequestRepository(); } public OAuth2AuthorizedClientService authorizedClientService( ) { JdbcOperations jdbcOperations = getApplicationContext().getBean(JdbcOperations.class); ClientRegistrationRepository clientRegistrationRepository = getApplicationContext().getBean( ClientRegistrationRepository.class); return new JdbcOAuth2AuthorizedClientService(jdbcOperations, clientRegistrationRepository); } public OAuth2AuthorizedClientRepository authorizedClientRepository() { return new AuthenticatedPrincipalOAuth2AuthorizedClientRepository(authorizedClientService()); } public OAuth2AuthenticationSuccessHandler oauth2AuthenticationSuccessHandler() { return new OAuth2AuthenticationSuccessHandler(jwt(), getApplicationContext().getBean(UserService.class)); }
- 객체 초기화가 끝나고 실행되는 configure 메서드에서는 위의 객체들을 bean이 아닌 직접 생성해서 OAuth2 설정에 등록시킵니다.
WebSecurityConfigure의 Configure
@Override protected void configure(HttpSecurity http) throws Exception { http /** * 중간 생략 */ .oauth2Login() .authorizationEndpoint() .authorizationRequestRepository(authorizationRequestRepository()) .and() .successHandler(oauth2AuthenticationSuccessHandler()) .authorizedClientService(authorizedClientService()) .authorizedClientRepository( authorizedClientRepository()) .and() ; }