[Spring] 구글 로그인 API 구현

Wonjun Seo·2023년 4월 5일
0

구글 설정

https://console.cloud.google.com/apis/dashboard 접속


  1. 상단 프로젝트 부분 클릭하기

  1. 새 프로젝트 > 프로젝트 이름 입력 > 만들기

  1. 프로젝트 선택 > OAuth 동의 화면 > 외부 > 만들기

  2. 필수 항목 작성 > 범위 설정 > 테스트 사용자 등록 > 요약


  1. 사용자 인증 정보 만들기 > OAuth 클라이언트 ID

  1. 애플리케이션 유형: 웹 애플리케이션 > 승인된 리디렉션 URI 입력

Spring 공식 문서 참고:

https://docs.spring.io/spring-security/reference/servlet/oauth2/login/core.html


  1. 생성된 클라이언트 ID와 비밀번호 확인

OAuth2 설정

build.gradle에 dependency 추가

implementation 'org.springframework.boot:spring-boot-starter-oauth2-client'

application.properties

spring.security.oauth2.client.registration.google.client-id=생성된 클라이언트 ID
spring.security.oauth2.client.registration.google.client-secret=클라이언트 보안 비밀번호
spring.security.oauth2.client.registration.google.scope=profile, email

패키지 > config > SecurityConfig.java

@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private PrincipalOauth2UserService principalOauth2UserService;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable()
                .authorizeRequests()
                .antMatchers("/", "/user/**", "/bus/join").permitAll()
                .antMatchers("/admin/**").hasRole("ADMIN")
                .antMatchers("/bus/**").hasRole("BUSINESS")
            .and() // 일반 로그인 처리
                .oauth2Login() // OAuth2 기반의 로그인 요청인 경우
                .loginPage("/user/login") // 지정된 url로 이동
                .userInfoEndpoint() // 로그인 성공후 사용자 정보를 가져옴
                .userService(principalOauth2UserService); // 사용자 정보를 처리할 때 사용함

    }

}

PrincipalOAuth2UserService 클래스 설정

패키지 > oauth > PrincipalOAuth2UserService.java

@Service
public class PrincipalOAuth2UserService extends DefaultOAuth2UserService {

    @Autowired
    private UserRepository userRepository;

    // userRequest는 code를 받아서 accessToken을 응답 받은 객체
    @Override
    public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException {
    
    	// google의 회원 프로필 조회
        OAuth2User oAuth2User = super.loadUser(userRequest);

        // code를 통해 구성한 정보
        System.out.println("userRequest clientRegistration : " + userRequest.getClientRegistration());
        
        // token을 통해 응답받은 회원정보
        System.out.println("oAuth2User : " + oAuth2User);

        return processOAuth2User(userRequest, oAuth2User);
    }

    private OAuth2User processOAuth2User(OAuth2UserRequest userRequest, OAuth2User oAuth2User) {

        OAuth2UserInfo oAuth2UserInfo = null;
        if (userRequest.getClientRegistration().getRegistrationId().equals("google")) {
            oAuth2UserInfo = new GoogleUserInfo(oAuth2User.getAttributes());
        }

		// DB에서 해당 정보를 가진 구글 사용자가 있는지 확인
        Optional<User> userOptional =
                userRepository.findByProviderAndProviderId(oAuth2UserInfo.getProvider(), oAuth2UserInfo.getProviderId());

        User user;
        // 사용자가 이미 존재하는 경우
        if (userOptional.isPresent()) {
            user = userOptional.get();
            user.updateEmail(oAuth2UserInfo.getEmail());
            userRepository.save(user);
        } else {
            // 해당 정보를 가진 사용자가 없으면, DB에 해당 유저의 정보를 저장
            user = User.builder()
                    .username(oAuth2UserInfo.getProvider() + "_" + oAuth2UserInfo.getProviderId())
                    .email(oAuth2UserInfo.getEmail())
                    .role(Role.SOCIAL)
                    .provider(oAuth2UserInfo.getProvider())
                    .providerId(oAuth2UserInfo.getProviderId())
                    .build();
            userRepository.save(user);
        }

        return new PrincipalDetails(user, oAuth2User.getAttributes());
    }
}

PrincipalDetails 클래스 구현

패키지 > config > auth > PrincipalDetails.java

OAuth2 User를 관리하기 위해서 기존에 있던 PrincipalDetails 클래스에서 OAuth2User도 implement 한다.

public class PrincipalDetails implements UserDetails, OAuth2User {

    private static final long serialVersionUID = 1L;
    private User user;
    private Map<String, Object> attributes;

    // 일반 시큐리티 로그인시 사용
    public PrincipalDetails(User user) {
        this.user = user;
    }

    // OAuth2.0 로그인시 사용
    public PrincipalDetails(User user, Map<String, Object> attributes) {
        this.user = user;
        this.attributes = attributes;
    }

    public User getUser() {
        return user;
    }

    @Override
    public String getPassword() {
        return user.getPassword();
    }

    @Override
    public String getUsername() {
        return user.getEmail();
    }

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

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

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

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

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        Collection<GrantedAuthority> authorities = new ArrayList<>();
        authorities.add(()->{ return user.getRoleKey();});
        return authorities;
    }

    // 리소스 서버로 부터 받는 회원정보
    @Override
    public Map<String, Object> getAttributes() {
        return attributes;
    }

    // User의 Primary Key
    @Override
    public String getName() {
        return user.getId() + "";
    }

}

0개의 댓글