본문 바로가기
오류를 개발새발

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

by 휴일이 2023. 1. 9.

 

 

스프링 시큐리티 강좌를 보는데 

순환 참조 에러가 발생했다....

(무한으로 사이클 돌려서 서로 참조)

 

 

 

SecurityConfig.class

@Configuration
@EnableWebSecurity // 스프링 시큐리티 필터가 스프링 필터 체인에 등록
@RequiredArgsConstructor
/**
 * secured
 * preAuthorize(postAuthorize)
 * 메소드에 직접 권한 걸기 true
 */
@EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    private final PrincipalOauth2UserService principalOauth2UserService;

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

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf()
                .disable()

                .authorizeRequests()
                .antMatchers("/user/**").authenticated() //인증만 받으면 접속 가능
                // 인증뿐만 아니라, 권한도 있어야함
                .antMatchers("/manager/**").access("hasRole('ROLE_ADMIN') or hasRole('ROLE_MANAGER')")
                .antMatchers("/admin/**").access("hasRole('ROLE_ADMIN')")
                .anyRequest().permitAll() //나머지 요청은 다 허용할게요

                .and()
                .formLogin()
                .loginPage("/loginForm") // 로그인 페이지 명시
//                .usernameParameter("id") // username 파라미터 이름 명시(기본 username)
                .loginProcessingUrl("/login") //시큐리티가 대신 로그인 진행해줌(컨트롤러에 /login 안 만들어도 됨)
                .defaultSuccessUrl("/") //로그인이 성공하면, 이 페이지로 가주세요

                /**
                 * 1. 코드 받기(인증)
                 * 2. 액세스 토큰 받기(권한)
                 * 3. 사용자 프로필 정보 가져오기
                 * 4-1. 정보를 토대로 자동 회원가입
                 * 4-2. 추가 정보 필요하다면 작성
                 */
                /**
                 * 구글 로그인?
                 * 액세스 토큰 + 사용자 프로필 정보 한꺼번에 가져옴
                 * 코드 필요 X
                 * username = google_(sub)
                 * password = (암호화)겟인데어
                 * email = 구글이메일
                 * role = ROLE_USER
                 */
                .and()
                .oauth2Login() //oauth2 로그인 허용
                .loginPage("/loginForm") //구글 로그인 인증 페이지는?
                .userInfoEndpoint() //로그인이 성공했다면?
                .userService(principalOauth2UserService) //후처리 이렇게 해주세요

        ;
    }
}

 

 

 

PrincipalOauth2UserService.class

@Slf4j
@Service
public class PrincipalOauth2UserService extends DefaultOAuth2UserService {

    private final BCryptPasswordEncoder bCryptPasswordEncoder;
    private final UserRepository userRepository;

    public PrincipalOauth2UserService(BCryptPasswordEncoder bCryptPasswordEncoder, UserRepository userRepository) {
        this.bCryptPasswordEncoder = bCryptPasswordEncoder;
        this.userRepository = userRepository;
    }

    //로그인 후처리
    // 구글로 받은 userRequest 데이터에 대한 후처리
    @Override
    public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException {
        log.info("userRequest = {}", userRequest);
        // registrationId 로 어떤 OAuth 로 로그인했는지 확인 가능
        log.info("getClientRegistration = {}", userRequest.getClientRegistration()); // 클라이언트가 누구니? (구글..)
        log.info("getAccessToken = {}", userRequest.getAccessToken()); // 토큰(토큰 정보)

        OAuth2User oAuth2User = super.loadUser(userRequest);
        // 구글 로그인 버튼 클릭 -> 구글 로그인 창 -> 로그인 완료 -> code 리턴(OAuth - Client 라이브러리) -> AccessToken 요청
        // userRequest 정보 -> loadUser() -> 구글로부터 회원 프로필 받아줌
        log.info("getAttributes = {}", super.loadUser(userRequest).getAttributes()); // 유저 값(이름, 이메일...) // sub=115894611263003886842 -> primaryKey , 구글 회원 아이디 PK

        // 회원 가입 강제 진행
        String provider = userRequest.getClientRegistration().getClientId(); //google
        String providerId = oAuth2User.getAttribute("sub");
        String username = provider + "_" + providerId; // google_115894611263003886842
        String password = bCryptPasswordEncoder.encode("겟인데어"); //의미는 없지만 그래도
        String email = oAuth2User.getAttribute("email");
        String role = "ROLE_USER";

        User userEntity = userRepository.findByUsername(username);
        // 찾은 userEntity가 없다며는 회워가입
        if (userEntity == null) {
            userEntity = User.builder()
                    .username(username)
                    .password(password)
                    .email(email)
                    .role(role)
                    .provider(provider)
                    .providerId(providerId)
                    .build();
            userRepository.save(userEntity);
        }

        // Authentication 객체 안으로 들어감
        return new PrincipalDetails(userEntity, oAuth2User.getAttributes());
    }
}

 

무한으로 즐겨요

 

 

 

┌─────┐
|  securityConfig defined in file [\SecurityConfig.class]
↑     ↓
|  principalOauth2UserService defined in file [C\PrincipalOauth2UserService.class]
└─────┘

 

SecurityConfig가 principalOauth2UserService 를 참조하고 있는데

principalOauth2UserService 에서는 BCryptPasswordEncoder 를 사용하려고 SecurityConfig 를 참조하고...

무한 반복으로 리사이클되는 에러 ㅡ.ㅡ.....

 

별거 다 해봤는데 이해를 잘 못하겠어서

기가맥힌 방법 알아냇다

 

 

 

@Bean
public static BCryptPasswordEncoder encoderPwd() {
    return new BCryptPasswordEncoder();
}

 

걍 패스워드 인코더 빈 주입할 때 스태틱 메소드로 생성하믄 댄다

아마 객체 생성 전에 이미 클래스 영역에 만들어놔서

순환 참조 되지 않는듯...?(잘 모름)

글구 스태틱은 싱글톤이니께뭐..ㅎㅎ 굳이 빈 생성 안 해도 되지 않나?(잘 모름)

 

아시는 분들은 댓글로 좀 알려주세요..

 

 

728x90