2023년 1월 1일
08:00 AM
Buffering ...

최근 글 👑

[스프링 시큐리티] Oauth2 구현방법 및 흐름

2024. 1. 31. 23:14ㆍ스프링 시큐리티 -세션/Oauth2.0

 

 

 

 

 

 

이번 포스팅은 Oauth2 구현방법 및 흐름을 알아볼려고 한다.

 

 

[스프링 시큐리티 Oauth2.0] Oauth2.0 동작방식 (tistory.com)

 

[스프링 시큐리티 Oauth2.0] Oauth2.0 동작방식

[소개] Oauth2를 사용하기 위해서 Oauth의 흐름을 알아보도록 한다. [Oauth2 사용자] Oauth의 경우 총 4명의 사용자들을 정의 할 수 있다. ● Resource Owner(사용자) : 일반적으로 사용자를 가르키며 소셜 서

thaud153.tistory.com

 

 

Oauth2의 전체적인 흐름은 여기 포스팅을 참고하면 될 것 이다.

 

 

[Oauth2 구현 방법]

 

이번 포스팅은 구글과 네이버의 구현방법을 설명할려고 한다.

 

 

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

 

Google 클라우드 플랫폼

로그인 Google 클라우드 플랫폼으로 이동

accounts.google.com

 

 

1. 구글 api 콘솔에 들어간다.

 

 

 

 

 

2. 동그라미 친 부분을 클릭한다.

 

 

 

 

 

3. 새 프로젝트 클릭

 

 

 

 

 

 

4. 만들기 클릭

 

 

 

 

5. 만들었으면 자신이 만든 프로젝트 클릭 나의 경우 TeamProject01

 

 

 

 

 

6. Oauth2 동의 화면 클릭 후 외부 선택 후 만들기

 

 

 

 

7. 개발자 이메일 및 앱이름 작성(앱이름은 아무거나 작성해도 됨)

 

 

 

 

 

8. api 범위 체크 email , Profile 후 업데이트 누르고

 

저장 및 계속

 

 

 

9.저장 후 계속

 

 

 

 

 

10. 사용자 인증정보 클릭 -> 사용자 인증 정보 만들기 -> Oauth2 클라이언트 Id 클릭

 

 

 

1.어플리케이션 유형 -> 웹 어플리케이션

 

2. 이름 -> 아무거나

 

3. 승인된 리디렉션(매우 중요) -> localhost:8081/login/oauth2/code/google <- 고정

 

-> 이 url은 고정이 되어야 한다. 

그 이유는 Oauth2의 경우 login/oauth2/code/{RegisterId}가 들어올 경우 

 

Oauth2LoginAuthorzaitonFilter라는 곳에 이 경로에 들어오면 Oauth2 로그인이 되었음을 확인함으로

이 코드는 반드시 저기 동그라미 친 부분과 고정되어야 한다.

 

ex) 만약 자신이 포트번호를 8080을 쓴다면 localHost8080으로 명시할 것.

 

나의 경우는 8081을 쓰기 때문에 8081로 적었음을 밝힘.

 

 

 

 

 

12. 생성되었다면 클라이언트 Id와 클라이언트 비밀번호가 나옴.

 

이 클라이언트 ID와 비밀번호를 반드시 기억할 것.

 

 

 

[Section02 네이버 콘솔 만들기]

 

네이버 개발자 센터 - NAVER Developers

 

NAVER Developers

네이버 오픈 API들을 활용해 개발자들이 다양한 애플리케이션을 개발할 수 있도록 API 가이드와 SDK를 제공합니다. 제공중인 오픈 API에는 네이버 로그인, 검색, 단축URL, 캡차를 비롯 기계번역, 음

developers.naver.com

 

 

 

 

 

 

 

1. 로그인을 한 후 Application -> 내 어플리케이션

 

 

 

 

 

2. 내 어플리케이션을 들어간 후 -> 애플리케이션 등록

 

 

 

 

 

3. 어플리케이션 이름 등록(아무거나) , 자신이 네이버 api로부터 가지고 오고싶은 정보들을 선택

(나의 경우 회원이름 이메일 , 별명 , 프로필 사진 4개를 가져올 것)

 

 

 

4.네이버의 리다이렉트 주소

 

이 리다이렉트 주소는 구글과 마찬가지로 고정임. 

 

자신이 포트번호가 8080이라면

 

http://localhost8080:/login/oauth2/code/naver

 

이 고정된 주소를 반드시 써야함.

 

 

 

 

 

5. 등록이 다 되었다면 클라이언트 아이디랑 시크릿 비밀번호를 반드시 기억할 것.

 

 

 

 

[Section 03 인텔리제이로 연동하기]

 

[구글의 경우]

 

 

1. 인텔리제이를 킨다.

 

 

2. Application properties를 들어간다.

 

#google

spring.security.oauth2.client.registration.google.client-id=955613786665-irnoig6r5kes6poqupqmpu8v6nug1hgb.apps.googleusercontent.com
spring.security.oauth2.client.registration.google.client-secret=your Secret Code
spring.security.oauth2.client.registration.google.scope=profile,email

 

 

자신이 등록했던 클라이언트 id와 클라이언트 비밀번호를 넣고 

scope방식은 자신이 등록했던 범위 만큼 넣으면  된다.

 

나의 경우는 구글에서 email과 profile 두 개를 가져오기로 설정했다.

 

 

[네이버의 경우]

 

 

#naver
spring.security.oauth2.client.registration.naver.client-id=QS3mYykThvib1IuIyFjZ
spring.security.oauth2.client.registration.naver.client-secret=your Secret Password
spring.security.oauth2.client.registration.naver.authorization-grant-type=authorization_code
spring.security.oauth2.client.registration.naver.client-name=naver-clinent-app
spring.security.oauth2.client.registration.naver.redirect-uri=http://localhost:8081/login/oauth2/code/naver
spring.security.oauth2.client.registration.naver.scope=profile,email

 

 

-> 구글이랑 다른점?

 

-> 구글의 경우는 글로벌 기업임으로 api 엔드포인트가 이미 시큐리티에 다 설정되어있다.

하지만 네이버의 경우 글로벌 기업이 아님으로 자신이 직접 엔드포인트 요청을 작성해야 한다.

 

public enum CommonOAuth2Provider {

   GOOGLE {

      @Override
      public Builder getBuilder(String registrationId) {
         ClientRegistration.Builder builder = getBuilder(registrationId,
               ClientAuthenticationMethod.CLIENT_SECRET_BASIC, DEFAULT_REDIRECT_URL);
         builder.scope("openid", "profile", "email");
         builder.authorizationUri("https://accounts.google.com/o/oauth2/v2/auth");
         builder.tokenUri("https://www.googleapis.com/oauth2/v4/token");
         builder.jwkSetUri("https://www.googleapis.com/oauth2/v3/certs");
         builder.issuerUri("https://accounts.google.com");
         builder.userInfoUri("https://www.googleapis.com/oauth2/v3/userinfo");
         builder.userNameAttributeName(IdTokenClaimNames.SUB);
         builder.clientName("Google");
         return builder;
      }

   },

 

GITHUB {

   @Override
   public Builder getBuilder(String registrationId) {
      ClientRegistration.Builder builder = getBuilder(registrationId,
            ClientAuthenticationMethod.CLIENT_SECRET_BASIC, DEFAULT_REDIRECT_URL);
      builder.scope("read:user");
      builder.authorizationUri("https://github.com/login/oauth/authorize");
      builder.tokenUri("https://github.com/login/oauth/access_token");
      builder.userInfoUri("https://api.github.com/user");
      builder.userNameAttributeName("id");
      builder.clientName("GitHub");
      return builder;
   }

},

 

 

FACEBOOK {

   @Override
   public Builder getBuilder(String registrationId) {
      ClientRegistration.Builder builder = getBuilder(registrationId,
            ClientAuthenticationMethod.CLIENT_SECRET_POST, DEFAULT_REDIRECT_URL);
      builder.scope("public_profile", "email");
      builder.authorizationUri("https://www.facebook.com/v2.8/dialog/oauth");
      builder.tokenUri("https://graph.facebook.com/v2.8/oauth/access_token");
      builder.userInfoUri("https://graph.facebook.com/me?fields=id,name,email");
      builder.userNameAttributeName("id");
      builder.clientName("Facebook");
      return builder;
   }

 

 

 

 

즉 이런식으로 구글 페이스북 깃허브의 경우 스프링에서 설정파일에 엔드포인트 요청이 다 적혀있다.

 

 

#naver
spring.security.oauth2.client.registration.naver.client-id=QS3mYykThvib1IuIyFjZ
spring.security.oauth2.client.registration.naver.client-secret=your Secret Password
spring.security.oauth2.client.registration.naver.authorization-grant-type=authorization_code
spring.security.oauth2.client.registration.naver.client-name=naver-clinent-app
spring.security.oauth2.client.registration.naver.redirect-uri=http://localhost:8081/login/oauth2/code/naver
spring.security.oauth2.client.registration.naver.scope=profile,email

 

 

1. client - id = 클라이언트 아이디는 자신이 네이버 Oauth를 만들었을 때 있는 생성된 id를 넣는다.

 

2. clinent-secret = 자신이 네이버 oauth2를 만들었을 때 생성된 시크릿 번호를 넣는다.

 

3. authorization-grant-type = Oauth2 인증 방식을 code방식으로 사용하겠다는 의미이다.

 

4.clint-name = 사용자에게 Oauth2 인증을 할때 어떤식으로 이름이 보일 것인지 설정 함.

 

5.redirect -url = 구글이나 네이버 등에서 요청 후 인증 방식을 어디로 보내야할지 정해주는 설정 파일

반드시 naver나 구글에 redircturl로 한 경로를 입력해야한다.(이 경로는 고정임)

 

6.scope = 네이버 api로 가져올 유저의 정보의 범위를 정하는 설정파일 

나의 경우 email과 profile만 가져올 것임.

 

 

registation의 정보이다.

즉 여기까지가 우리가 naver나 google에서 필요한 클라이언트 정보를 입력하는 곳

----------------------------------------------------------------------------------

 

 

이 부분부터는 본격적으로 네이버에 api의 정보를 넣는 곳이다.

즉 저기 적힌 url경로에 따라 토큰이나 엔드포인트 정보를 가져옴.

 

spring.security.oauth2.client.provider.naver.authorization-uri=https://nid.naver.com/oauth2.0/authorize

spring.security.oauth2.client.provider.naver.token-uri=https://nid.naver.com/oauth2.0/token
spring.security.oauth2.client.provider.naver.user-info-uri=https://openapi.naver.com/v1/nid/me

spring.security.oauth2.client.provider.naver.user-name-attribute=response

 

 

1. authorization-uri = 네이버의 인증 서버의 주소이다.

 

2.token - uri = 액세스 토큰을 가져오기 위한 url

 

3.user-info-url = 액세스 토큰으로 유저의 정보를 가져오기 위한 url 경로이다.

 

4.user-name-attibutes = 네이버의 경우 Map<String,Object>형식으로 들어오는데

이 key값을 response로 받겠다는 의미이다.

(네이버의 경우 user-info정보가 키 값으로 response로 들어온다.)

(구글의 경우 키 값이 sub)로 들어옴.

 

 

5. 이까지 했으면 네이버 , 구글에게 자신의 클라이언트 정보와 , 엔드포인트 요청 url경로까지 다 적어주었다.

 

 

 

 

[Section 04 구현하기]

 

 

 

 

 

 

1. ProviderUser는 최상위 인터페이스로 NaverUser와 GoogleUser를 묶는 최상위 인터페이스임.

 

2.Abstractprovider는 구글과 네이버는 공통적으로 들어오는 정보도 있지만 틀리게 들어오는 정보도 있음.

이러한 정보들을 묶어주는 클래스임.

 

 

package com.example.oauth2test_01_26.Model;

import org.springframework.security.core.GrantedAuthority;

import java.util.List;
import java.util.Map;

public interface ProviderUser {


    String getId();
    String getUsername();
    String email();
    String provider();
    List<? extends GrantedAuthority> getAuthorization();
    Map<String,Object> getAttributes();
}

 

 

1. 최상위 인터페이스 providerUser를 만듬.

 

2.이 인터페이스에서는 이러한 정보들을 가져올 것 임.

 

package com.example.oauth2test_01_26.Model;

import com.example.oauth2test_01_26.Converter.ConverterAttributes;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.oauth2.client.registration.ClientRegistration;
import org.springframework.security.oauth2.core.user.OAuth2User;

import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

public abstract class AbstractProvider implements ProviderUser{

    private ClientRegistration registration;
    private OAuth2User user;
    private Map<String,Object> attributes;
    public AbstractProvider(ClientRegistration registration, OAuth2User user, Map<String,Object> attributes) {
        this.registration = registration;
        this.user = user;
        this.attributes = attributes;

    }

    @Override
    public String getUsername() {
        return null;
    }

    @Override
    public String email() {
        return attributes.get("email").toString();
    }

    @Override
    public String provider() {
        return registration.getRegistrationId();
    }

    @Override
    public List<? extends GrantedAuthority> getAuthorization() {
        return user.getAuthorities().stream().map(auth -> new SimpleGrantedAuthority(auth.getAuthority())).collect(Collectors.toList());
    }

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

 

 

2.이 추상클래스에서는 username , email , provider , Authorization , getAttribute등을 가져오는 추상클래스임

이 메서드들은 네이버와 유저 구글이 공통으로 들어가는 부분이기에 추상클래스에서 구현

 

 

[구글 클래스]

 

package com.example.oauth2test_01_26.Model;

import com.example.oauth2test_01_26.Converter.ConverterAttributes;
import org.springframework.security.oauth2.client.registration.ClientRegistration;
import org.springframework.security.oauth2.core.user.OAuth2User;

public class GoogleUser extends AbstractProvider{


    public GoogleUser(ClientRegistration registration, OAuth2User user, ConverterAttributes converterAttributes){
        super(registration,user,converterAttributes.getGoogleMap());
    }
    @Override
    public String getId() {
        return getAttributes().get("sub").toString();
    }
}

 

 

1. 구글 클래스에서는 id의 키값이 sub로 들어오기 때문에 네이버랑 다름.

그렇기 떄문에 id메서드를 따로 구현해서 꺼냄.

 

 

 

[네이버 클래스]

 

package com.example.oauth2test_01_26.Model;

import org.springframework.security.oauth2.client.registration.ClientRegistration;
import org.springframework.security.oauth2.core.user.OAuth2User;

import java.util.Map;

public class NaverUser extends AbstractProvider{


    public NaverUser(ClientRegistration registration, OAuth2User user, Map<String, Object> attributes) {
        super(registration, user, attributes);
    }

    @Override
    public String getId() {
        return getAttributes().get("id").toString();
    }

    @Override
    public String getUsername() {
        return super.getAttributes().get("nickname").toString();
    }
}

 

 

1. 네이버의 클래스의 경우 id를 꺼내는 키값이 id임.

 

2.네이버에서는 닉네임도 들어옴. 그렇기 때문에 nickName으로 꺼낼 시

네이버에 저장된 닉네임도 가져올 수 있음.

 

 

[Section 04 구글과 네이버의 컨버터 만들기 ]

 

 

1. ProviderUserRequest

 

만약 스프링에서 Oauth2 유저정보와 ClientRegistration을 가져올 시

제일먼저 ProviderUserRequest의 저장 해서 이 객체에 있는 정보들로

구글과 네이버등의 유저정보들을 컨버팅 할 것 임.

 

2. AbstractDelegatingOauth2Converter 

 

이 클래스는 권한을 부여하는 추상 클래스로써

 

구글이나 네이버의 oauth2 인증방식이 들어왔을 시 이 객체를 거쳐

이 객체가 적절한 판단을 한 후 NaverUserConverter 혹은 GoogleUserConverter에 권한을 위임할 것.

 

3.NaverUserConverter 

 

네이버 Oauth2로 들어올 시 네이버의 정보를 컨버팅해서 NaverUser에 담을 것임.

 

4.GoogleUserConver

 

구글 Oauth2로 들어올 시 구글의 정보를 컨버팅해서 GoogleUser의 담을 것 임

 

 

 

package com.example.oauth2test_01_26.Converter;

public interface ProviderOauth2Converter <T,R>{

    R converter(T t);
}

 

 

1.최상위 인터페이스를 제네릭으로 선언함.

 

 

 

package com.example.oauth2test_01_26.Converter;

import org.springframework.security.oauth2.client.registration.ClientRegistration;
import org.springframework.security.oauth2.core.user.OAuth2User;

public record ProviderUserRequest(ClientRegistration registration, OAuth2User user) {

}

 

 

 

2.record형식으로 선언되었으며 생성자에 ClientRegistration과 Oauth2를 넣으면

 

자동으로 필드객체에 ClientRegstration과 Oauth2의 주소정보 (개체 정보)를 넣는다.

 

 

 

 

package com.example.oauth2test_01_26.Converter;

import com.example.oauth2test_01_26.Model.GoogleUser;
import com.example.oauth2test_01_26.Model.ProviderUser;

import java.util.Arrays;
import java.util.List;

public abstract class AbstractDelegatingOauth2Converter implements ProviderOauth2Converter<ProviderUserRequest, ProviderUser>{

    private List<? extends ProviderOauth2Converter> list;

    //초기화와 동시에 List객체에 구글 컨버터와 네이버의 컨버터를 개체를 저장함.
    public AbstractDelegatingOauth2Converter(){
        List<? extends ProviderOauth2Converter> list = Arrays.asList(new GoogleUserConverter(),new NaverUserConverter());
        this.list = list;
    }

    @Override
    public ProviderUser converter(ProviderUserRequest request) {

        //이 객체를 저장해서 이 개체를 컨버터 하면서 컨버팅(구글 유저 -> 구글 컨버팅 , 네이버 유저 -> 네이버 컨버팅)
        //네이버에 유저가 들어올 시 -> 네이버 컨버팅 -> NaverUser객체를 뱉음.
        //구글에 유저가 들어올 시 -> 구글 컨버팅 -> GoogleUser 객체를 뱉음 
        for (ProviderOauth2Converter providerOauth2Converter : list) {
            ProviderUser converter = (ProviderUser) providerOauth2Converter.converter(request);

            if(converter != null) return  converter;
        }
        return null;
    }
}

 

 

2.AbstractDelegatingOauth2Converter 

 

권한을 위임하는 클래스며

 

구글로 유저가 들어오면 구글 컨버팅이 동작하게 됨.

 

네이버로 유저가 들어오면 네이버로 컨버팅이 동작하게 됨.

 

 

package com.example.oauth2test_01_26.Converter;


import lombok.Builder;
import lombok.Getter;
import org.springframework.security.oauth2.core.user.OAuth2User;

import java.util.Map;

@Builder
@Getter
public class ConverterAttributes {

    private final Map<String,Object> googleMap;
    private final Map<String,Object> naverMap;



    public static ConverterAttributes GoogleAttributesConverter(OAuth2User user){

      return   ConverterAttributes.builder().googleMap(user.getAttributes()).build();
    }
    public static ConverterAttributes NaverAttributesConverter(OAuth2User user){
        Map<String, Object> response = (Map<String, Object>) user.getAttributes().get("response");
        return ConverterAttributes.builder().naverMap(response).build();
    }

}

 

 

3. 네이버나 구글의 정보가 들어왔을 시 Map형태에서 빼서 가공된 데이터를 

 

전역변수 Map에 넣어주는 역활을 함.

 

구글의 경우 sub로 들어오고

 

네이버의 경우 response의 2단으로 Map형태로 들어오기 때문에

 

키로 2번을 뺴줘야 함.

 

package com.example.oauth2test_01_26.Converter;


import com.example.oauth2test_01_26.Model.GoogleUser;
import com.example.oauth2test_01_26.Model.ProviderUser;

public class GoogleUserConverter implements ProviderOauth2Converter<ProviderUserRequest, ProviderUser>{

    @Override
    public ProviderUser converter(ProviderUserRequest request) {

        //프로바이더정보가 구글이 아니면 네이버이기 때문에 return null을 줌
        if(!request.registration().getRegistrationId().equals("google")) return null;
        
        ConverterAttributes converterAttributes = ConverterAttributes.GoogleAttributesConverter(request.user());

        
        //유저가 구글로 들어왔을 때 구글의 정보를 GoogleUser로 리턴
        return new GoogleUser(request.registration(), request.user(),converterAttributes);
    }
}

 

 

 

 

package com.example.oauth2test_01_26.Converter;

import com.example.oauth2test_01_26.Model.NaverUser;
import com.example.oauth2test_01_26.Model.ProviderUser;

public class NaverUserConverter implements ProviderOauth2Converter<ProviderUserRequest, ProviderUser>{

    @Override
    public ProviderUser converter(ProviderUserRequest request) {

        
        //Provider정보가 네이버가 아니면 구글이기때문에 return null
        if(!request.registration().getRegistrationId().equals("naver")) return null;

        ConverterAttributes converterAttributes = ConverterAttributes.NaverAttributesConverter(request.user());


        
        //유저가 네이버임이 확인되면 NaverUser에서 네이버(유저정보) 정보로 넣음
        return new NaverUser(request.registration(),request.user(),converterAttributes.getNaverMap());
    }
}

 

 

 

 

 

 

package com.example.oauth2test_01_26.Service;

import com.example.oauth2test_01_26.Table.UserInformation;
import com.nimbusds.openid.connect.sdk.claims.UserInfo;
import lombok.Data;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.oauth2.core.user.OAuth2User;


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

@Data
public class UserService implements UserDetails, OAuth2User {

    private UserInformation information;

    public UserService(UserInformation information) {
        this.information = information;
    }



    @Override
    public <A> A getAttribute(String name) {
        return OAuth2User.super.getAttribute(name);
    }

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return information.getAuthorities();
    }

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

    @Override
    public String getUsername() {
        return information.getUserNickName();
    }

    @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 Map<String, Object> getAttributes() {
        return information.getProviderUser().getAttributes();
    }

    @Override
    public String getName() {
        return information.getUserNickName();
    }
}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

package com.example.oauth2test_01_26.Service;


import com.example.oauth2test_01_26.Converter.ProviderOauth2Converter;
import com.example.oauth2test_01_26.Converter.ProviderUserRequest;
import com.example.oauth2test_01_26.Model.ProviderUser;
import com.example.oauth2test_01_26.Repository.UserRepository;
import com.example.oauth2test_01_26.Table.UserEntity;
import com.example.oauth2test_01_26.Table.UserInformation;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import org.springframework.security.oauth2.core.user.OAuth2User;

@Getter
@RequiredArgsConstructor
public abstract class AbstractOauth2UserService {

    private final UserRepository repository;
    private final ProviderOauth2Converter<ProviderUserRequest, ProviderUser> converter;


    protected ProviderUser Converter(ProviderUserRequest request){
        ProviderUser converter1 = converter.converter(request);
        return converter1;
    }
    protected void register(ProviderUser user){

        UserEntity build = UserEntity.builder().provider(user.provider())
                .username(user.getUsername())
                .email(user.email())
                .build();
        repository.save(build);
    }
    protected  UserInformation TransferUserInformation(ProviderUser user){
        if(user.provider().equals("naver")){
         return    UserInformation.builder().userNickName(user.getUsername())
                    .providerUser(user).passWord(null)
                    .email(user.email())
                    .authorities(user.getAuthorization())
                    .build();
        }else {
            return    UserInformation.builder().userNickName("닉네임 없음")
                    .providerUser(user).passWord(null)
                    .email(user.email())
                    .authorities(user.getAuthorization())
                    .build();
        }

    }
}

 

 

 

1.TransferUserInofrmation부분은 유저의 정보를 시큐리티에서 꺼낼 때

일반 폼로그인 유저와 , Oauth 유저의 정보를 통합하기 위해 UserInfomation클래스에 넣어서

함께 묶어서 쓰기위함이다.

 

package com.example.oauth2test_01_26.Table;


import com.example.oauth2test_01_26.Converter.ProviderUserRequest;
import com.example.oauth2test_01_26.Model.ProviderUser;
import lombok.Builder;
import lombok.Data;
import org.springframework.security.core.GrantedAuthority;

import java.util.List;

@Data
public class UserInformation {
    private String userNickName;
    private String passWord;
    private List<? extends GrantedAuthority> authorities;
    private String email;
    private ProviderUser providerUser;

    @Builder
    public UserInformation( String userNickName, String passWord, List<? extends GrantedAuthority> authorities, String email, ProviderUser providerUser) {
        this.userNickName = userNickName;
        this.passWord = passWord;
        this.authorities = authorities;
        this.email = email;
        this.providerUser = providerUser;
    }
}

 

 

이 부분에 UserInformation부분이며

1. 폼로그인 방식과 Oauth2의 유저 정보를 함께 공유하기 위해 사용되는 클래스임

이렇게 하지 않으면 폼로그인과 Oauth2유저가 묶이지 않아서

 

폼로그인 유저가 들어왔을때 Oauth2과 관련된 정보를 꺼낸다면 NPE가 뜸

 

 

package com.example.oauth2test_01_26.Service;

import com.example.oauth2test_01_26.Converter.ProviderOauth2Converter;
import com.example.oauth2test_01_26.Converter.ProviderUserRequest;
import com.example.oauth2test_01_26.Model.ProviderUser;
import com.example.oauth2test_01_26.Repository.UserRepository;
import com.example.oauth2test_01_26.Table.UserInformation;
import org.springframework.security.oauth2.client.registration.ClientRegistration;
import org.springframework.security.oauth2.client.userinfo.DefaultOAuth2UserService;
import org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest;
import org.springframework.security.oauth2.client.userinfo.OAuth2UserService;
import org.springframework.security.oauth2.client.web.OAuth2LoginAuthenticationFilter;
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.stereotype.Service;

@Service
public class CustomOauth2UserService extends AbstractOauth2UserService implements OAuth2UserService<OAuth2UserRequest, OAuth2User> {

    public CustomOauth2UserService(UserRepository repository, ProviderOauth2Converter<ProviderUserRequest, ProviderUser> converter) {
        super(repository, converter);
    }

    @Override
    //여기에 들어오는 userRequest 액세스토큰과 , 코드등이 들어있음 유저 정보는 X
    public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException {
        ClientRegistration clientRegistration = userRequest.getClientRegistration();

        OAuth2UserService<OAuth2UserRequest,OAuth2User> oAuth2UserService
                = new DefaultOAuth2UserService();



        //이 메서드 호출로 유저의 정보를 가져옴.
        //이 메서드 호출로 oauth2User에는 유저 정보가 들어있음.
        OAuth2User oAuth2User = oAuth2UserService.loadUser(userRequest);


        //우리가 만든 ProviderUserRequest 이안에 Client와 Oauth2(유저 정보)등을 저장함.
        ProviderUserRequest providerUserRequest = new ProviderUserRequest(clientRegistration, oAuth2User);


        //각 Oauth2맞는 값들은 변환
        ProviderUser converter = super.Converter(providerUserRequest);

        super.register(converter);

        //oauth2와 폼데이터 방식(자체 회원가입)을 함께 묶어서 사용하기 위한 로직
        UserInformation userInformation = super.TransferUserInformation(converter);

        //이 함께 묶은 정보를 UserService에 저장함
        UserService userService = new UserService(userInformation);

        return userService;
    }
}

 

 

 

 

 

여기까지 정리했다면 이제 Config를 만들어줘야한다.

 

-> 그이유는 ProviderOauth2Converter의 최상위 인터페이스가 있는데

이 인터페이스 안에 AbstractDelegatingOauth2Converter부분을 의존성을 주입시켜줘야한다.

 

그래야만  ProviderOauth2Converter의 Converter가 실행되었을 때 

 

AbstractDelegatingOauth2Converter이 개체가 실행된다.

 

 

 

package com.example.oauth2test_01_26;


import com.example.oauth2test_01_26.Converter.AbstractDelegatingOauth2Converter;
import com.example.oauth2test_01_26.Converter.ProviderOauth2Converter;
import com.example.oauth2test_01_26.Converter.ProviderUserRequest;
import com.example.oauth2test_01_26.Model.ProviderUser;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;

@EnableWebSecurity
@Configuration
public class SecurityConfig {



    @Bean
    public ProviderOauth2Converter<ProviderUserRequest, ProviderUser> converter(){

        return new AbstractDelegatingOauth2Converter() {
            @Override
            public ProviderUser converter(ProviderUserRequest request) {
                return super.converter(request);
            }
        };
    }
}