인프런 커뮤니티 질문&답변

유은혜님의 프로필 이미지

작성한 질문수

스프링부트 시큐리티 & JWT 강의

[Google oauth2 관련] The dependencies of some of the beans in the application context form a cycle

작성

·

810

·

수정됨

0

에러 메시지는 아래와 같습니다.

***************************
APPLICATION FAILED TO START
***************************

Description:

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

┌─────┐
|  oauth2UserService defined in file [D:\GooGoo\out\production\classes\eunhye\GooGoo\config\oauth\Oauth2UserService.class]
↑     ↓
|  securityConfig defined in file [D:\GooGoo\out\production\classes\eunhye\GooGoo\config\security\SecurityConfig.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

oauth2UserService와 securityConfig가 cycle을 이루고 있다.

뭔가 구조가 잘못됐다는 것을 알 수 있었습니다만...

전 강의 잘 듣고 잘 코드 쳤다고 생각이 되거든요ㅠ

스스로를 의심하며 아무리 코드를 쳐다봐도 잘못된 점을 모르겠어서 질문 올립니다.

다음은 관련된 코드들입니다.

 

Oauth2UserService.java

package eunhye.GooGoo.config.oauth;

import eunhye.GooGoo.config.security.SecurityDetails;
import eunhye.GooGoo.dto.UserDTO;
import eunhye.GooGoo.entity.UserEntity;
import eunhye.GooGoo.entity.UserRole;
import eunhye.GooGoo.repository.UserRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.client.userinfo.DefaultOAuth2UserService;
import org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest;
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.stereotype.Service;

@Service
@RequiredArgsConstructor
public class Oauth2UserService extends DefaultOAuth2UserService {

    private final UserRepository userRepository;
    private final PasswordEncoder passwordEncoder;

    // 구글로부터 받은 userRequest 데이터에 대해 후처리하는 함수
    @Override
    public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException{
        OAuth2User oauth2User = super.loadUser(userRequest);

        // 회원가입 강제 진행
        String provider = userRequest.getClientRegistration().getClientId();
        String providerId = oauth2User.getAttribute("sub");
        String userNickname = provider+"_"+providerId;
        String userEmail = oauth2User.getAttribute("email");
        String userPassword = passwordEncoder.encode("겟인데어");
        UserRole authority = UserRole.USER;

        UserEntity userEntity = userRepository.findByUserEmail(userEmail);

        if(userEntity == null){
            userEntity = UserEntity.builder()
                    .userNickname(userNickname)
                    .userEmail(userEmail)
                    .userPassword(userPassword)
                    .authority(authority)
                    .provider(provider)
                    .providerId(providerId)
                    .build();
            userRepository.save(userEntity);
        }

        return new SecurityDetails(userEntity, oauth2User.getAttributes());
    }
}

 

SecurityConfig.java

package eunhye.GooGoo.config.security;

import eunhye.GooGoo.config.oauth.Oauth2UserService;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;

@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class SecurityConfig{

    private final AuthenticationFailureHandler customFailureHandler;
    private final Oauth2UserService oauth2UserService;

    @Bean
    public BCryptPasswordEncoder passwordEncoder() {

        return new BCryptPasswordEncoder();
    }

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
                .csrf().disable()
                .authorizeRequests()
                .antMatchers("/user/**").authenticated()
                .antMatchers("/admin/**").access("hasRole('ADMIN')")
                .anyRequest().permitAll()
                .and()
                .formLogin()
                .loginPage("/login")
                .loginProcessingUrl("/login")
                .defaultSuccessUrl("/home")
                .usernameParameter("userEmail").passwordParameter("userPassword")
                .failureHandler(customFailureHandler)
                .and()
                .logout()
                .logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
                .logoutSuccessUrl("/login")
                .and().oauth2Login()
                .loginPage("/login").defaultSuccessUrl("/home")
                .userInfoEndpoint().userService(oauth2UserService);
        return http.build();
    }
}

 

SecurityDetails.java

package eunhye.GooGoo.config.security;

import eunhye.GooGoo.entity.UserEntity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.RequiredArgsConstructor;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.oauth2.core.user.OAuth2User;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Map;

@Data
public class SecurityDetails implements UserDetails, OAuth2User {

    // 일반 로그인
    private final UserEntity userEntity;

    // OAuth 로그인 (Google)
    private Map<String, Object> attributes;

    public SecurityDetails(UserEntity userEntity){
        this.userEntity = userEntity;
    }

    public SecurityDetails(UserEntity userEntity, Map<String, Object> attributes){
        this.userEntity = userEntity;
        this.attributes = attributes;
    }

    @Override
    public Map<String, Object> getAttributes() {
        return attributes;
    }

    @Override
    public String getName() {
        return userEntity.getId()+"";
    }

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        Collection<GrantedAuthority> authorities = new ArrayList<>();
        authorities.add(new GrantedAuthority() {
            @Override
            public String getAuthority() {
                return userEntity.getAuthority().toString();
            }
        });
        return authorities;
    }

    @Override
    public String getPassword() {

        return userEntity.getUserPassword();
    }

    @Override
    public String getUsername() {

        return userEntity.getUserEmail();
    }

    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

    @Override
    public boolean isAccountNonLocked() {
        return true;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

    @Override
    public boolean isEnabled() {
        return true;
    }
}

 

 추가적인 코드가 필요하다면 아래 깃헙 링크 참고해주세요

https://github.com/you-eun-hye/GooGoo

답변 1

1

유은혜님의 프로필 이미지
유은혜
질문자

SecurityConfig에 있는 PasswordEncoder 생성자 코드를 Application.java로 옮겨주니 에러가 해결되었습니다!

감사합니다. 덕분에 해결했습니다. 왜 해결됏는지 이유는모르겠지만..감사합니다