개발자의 공부방/스프링

스프링시큐리티] 순환 참조 에러

  • -
728x90
반응형

스프링시큐리티를 적용하는 중에 아래와 같은 에러가 발생했다.

 

에러 메시지

*************************** APPLICATION FAILED TO START *************************** Description: The dependencies of some of the beans in the application context form a cycle: ┌─────┐ | webSecurityConfig defined in file [/Users/june/Repository/june/out/production/classes/toyblog/june/springbootdev/config/WebSecurityConfig.class] ↑ ↓ | userService defined in file [/Users/june/Repository/june/out/production/classes/toyblog/june/springbootdev/service/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.

 

 

WebSecurityConfig.java

package toyblog.june.springbootdev.config; import lombok.RequiredArgsConstructor; import org.springframework.boot.autoconfigure.security.servlet.PathRequest; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.HttpStatus; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer; import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; import org.springframework.security.config.annotation.web.configurers.FormLoginConfigurer; import org.springframework.security.config.annotation.web.configurers.HttpBasicConfigurer; import org.springframework.security.config.annotation.web.configurers.LogoutConfigurer; import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.authentication.HttpStatusEntryPoint; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import org.springframework.security.web.util.matcher.AntPathRequestMatcher; import toyblog.june.springbootdev.config.jwt.TokenProvider; import toyblog.june.springbootdev.config.oauth.OAuth2AuthorizationRequestBseOnCookieRepository; import toyblog.june.springbootdev.config.oauth.OAuth2SuccessHandler; import toyblog.june.springbootdev.config.oauth.OAuth2UserCustomService; import toyblog.june.springbootdev.repository.RefreshTokenRepository; import toyblog.june.springbootdev.service.UserService; @RequiredArgsConstructor @Configuration public class WebSecurityConfig { private final OAuth2UserCustomService oAuth2UserCustomService; private final TokenProvider tokenProvider; private final RefreshTokenRepository refreshTokenRepository; private final UserService userService; @Bean public WebSecurityCustomizer configure() { return (web) -> web.ignoring() .requestMatchers(PathRequest.toH2Console()) .requestMatchers("/img/**", "/css/**", "/js/**"); } public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http.csrf(AbstractHttpConfigurer::disable) .httpBasic(HttpBasicConfigurer::disable) .formLogin(FormLoginConfigurer::disable) .logout(LogoutConfigurer::disable); http.sessionManagement(configure -> configure.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) .addFilterBefore(tokenAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class); http.authorizeHttpRequests(authorize -> authorize.requestMatchers("/api/token").permitAll() .requestMatchers("/api/**").authenticated() .anyRequest().permitAll()); http.oauth2Login(oauth2Login -> oauth2Login.loginPage("/page") .authorizationEndpoint(authorizationEndpoint -> authorizationEndpoint.authorizationRequestRepository(oAuth2AuthorizationRequestBseOnCookieRepository())) .successHandler(oAuth2SuccessHandler()) .userInfoEndpoint(userInfoEndpoint -> userInfoEndpoint.userService(oAuth2UserCustomService))); http.logout(logout -> logout .logoutSuccessUrl("/login")); http.exceptionHandling(exceptionHandling -> exceptionHandling.defaultAuthenticationEntryPointFor( new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED), new AntPathRequestMatcher("/api/**"))); return http.build(); } @Bean public OAuth2SuccessHandler oAuth2SuccessHandler() { return new OAuth2SuccessHandler(tokenProvider , refreshTokenRepository , oAuth2AuthorizationRequestBseOnCookieRepository() , userService); } @Bean public TokenAuthenticationFilter tokenAuthenticationFilter() { return new TokenAuthenticationFilter(tokenProvider); } // * 쿠키에 저장할 인증 요청 및 상태 저장소 @Bean public OAuth2AuthorizationRequestBseOnCookieRepository oAuth2AuthorizationRequestBseOnCookieRepository() { return new OAuth2AuthorizationRequestBseOnCookieRepository(); } @Bean public BCryptPasswordEncoder bCryptPasswordEncoder() { return new BCryptPasswordEncoder(); } }

 

 

UserService.java

package toyblog.june.springbootdev.service; import lombok.RequiredArgsConstructor; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.stereotype.Service; import toyblog.june.springbootdev.domain.User; import toyblog.june.springbootdev.dto.AddUserRequest; import toyblog.june.springbootdev.repository.UserRepository; @Service @RequiredArgsConstructor public class UserService { private final UserRepository userRepository; private final BCryptPasswordEncoder bCryptPasswordEncoder; public Long save(AddUserRequest addUserRequest) { return userRepository.save(User.builder() .email(addUserRequest.getEmail()) .password(bCryptPasswordEncoder.encode(addUserRequest.getPassword())) .build()) .getId(); } public User findById(Long userId) { return userRepository.findById(userId) .orElseThrow(() -> new IllegalArgumentException("Unexpected user")); } public User findByEmail(String email) { return userRepository.findByEmail(email) .orElseThrow(() -> new IllegalArgumentException("Unexpected user")); } }

 

 

원인

WebSecurityConfig.java에서 OAuth2SuccessHandler를 생성할 때 UserService를 주입하고 있고

UserService.java에서 bCryptPasswordEncoder를 직접 생성하고 있어서 문제가 되는 부분이었다.

 

원인 코드

@Service @RequiredArgsConstructor public class UserService { private final UserRepository userRepository; private final BCryptPasswordEncoder bCryptPasswordEncoder; // * 1) 원인 public Long save(AddUserRequest addUserRequest) { return userRepository.save(User.builder() .email(addUserRequest.getEmail()) // * 2) 원인. .password(bCryptPasswordEncoder.encode(addUserRequest.getPassword())) .build()) .getId(); }

 

 

해결 방법

BCryptPasswordEncoder를 필드 주입이 아닌 인스턴스를 직접 생성해서 넣어준다

public Long save(AddUserRequest addUserRequest) { BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder(); return userRepository.save(User.builder() .email(addUserRequest.getEmail()) .password(bCryptPasswordEncoder.encode(addUserRequest.getPassword())) .build()) .getId(); }
반응형
Contents

포스팅 주소를 복사했습니다

이 글이 도움이 되었다면 공감 부탁드립니다.