ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • SpringSecurity OAuth2.0 - 소셜로그인 구현
    BackEnd/SpringSecurity 2023. 7. 26. 00:53

    1. SecurityFilterChain 설정

    목적 : 소셜로그인을 위해 구현한 커스텀 필터 설정

     

    package io.security.oauth2.springsecurityoauth2.config.security;

    import io.security.oauth2.springsecurityoauth2.service.CustomOAuth2UserService;
    import io.security.oauth2.springsecurityoauth2.service.CustomOidcUserService;
    import org.springframework.beans.factory.annotation.Autowired;
    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.WebSecurityCustomizer;
    import org.springframework.security.web.SecurityFilterChain;
    import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;
    import org.springframework.security.web.authentication.logout.SimpleUrlLogoutSuccessHandler;

    @Configuration
    public class OAuth2ClientConfig {

    @Autowired 
    private CustomOAuth2UserService customOAuth2UserService;

    @Autowired
    private CustomOidcUserService customOidcUserService;

    @Bean
    public WebSecurityCustomizer webSecurityCustomizer() {
    return (web -> web.ignoring().antMatchers().antMatchers("/static/js/*", "static/images/**", "/static/css/**", "/static/icomoon/**")); // static 파일들 허용
    }


    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
    http
    .authorizeRequests(authRequest -> authRequest
    .antMatchers("/").permitAll() // 테스트를 위해서 모든 요청 허용
    .anyRequest().authenticated());

    http
    .formLogin().loginPage("/login").loginProcessingUrl("/loginProc").defaultSuccessUrl("/").permitAll();

    http
    .oauth2Login(oauth2 -> oauth2
    // .loginPage("/") 필요시 로그인 페이지 변경 (SPA는 페이지 이동 자체가 기본 원칙 위배로 사용X)
    .userInfoEndpoint(userInfoEndpointConfig -> userInfoEndpointConfig
    .userService(customOAuth2UserService) // 기본적인 authorization_code 방식 서비스 커스텀
    .oidcUserService(customOidcUserService))); // openid connect 방식 서비스 커스텀

    http
    .logout().logoutSuccessHandler(new SimpleUrlLogoutSuccessHandler()); // 로그아웃 원하는 작업시 커스텀

    http
    .exceptionHandling().authenticationEntryPoint(new LoginUrlAuthenticationEntryPoint("/login"));

    return http.build();
    }

     

    2. GrantedAuthoritiesMapper 설정

    목적 : 소셜로그인 계정에 커스텀 권한 설정

     

    package io.security.oauth2.springsecurityoauth2.config.security;

    import io.security.oauth2.springsecurityoauth2.common.authority.CustomGrantedAuthoritiesMapper;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper;

    @Configuration
    public class OAuth2AppConfig {

    @Bean
    public GrantedAuthoritiesMapper customAuthorityMapper() {
    return new CustomGrantedAuthoritiesMapper();
    }
    }

     

    package io.security.oauth2.springsecurityoauth2.common.authority;

    import org.springframework.security.core.GrantedAuthority;
    import org.springframework.security.core.authority.SimpleGrantedAuthority;
    import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper;

    import java.util.Collection;
    import java.util.HashSet;

    public class CustomGrantedAuthoritiesMapper implements GrantedAuthoritiesMapper {

    private String prefix = "ROLE_";

    @Override
    public Collection<? extends GrantedAuthority> mapAuthorities(Collection<? extends GrantedAuthority> authorities) {

    HashSet<GrantedAuthority> mapped = new HashSet<>(authorities.size());
    for (GrantedAuthority authority : authorities) {
    mapped.add(mapAuthority(authority.getAuthority()));
    }
    return mapped;
    }

    private GrantedAuthority mapAuthority(String name) {

    if (name.lastIndexOf(".") > 0) { // 각 소셜 로그인 인가서버로 부터 받은 정보를 파싱해서 새로운 권한 정보 생성
    int index = name.lastIndexOf(".");
    name = "SCOPE_" + name.substring(index + 1);
    }

    if (prefix.length() > 0 && !name.startsWith(prefix)) {
    name = prefix + name;
    }

    return new SimpleGrantedAuthority(name);
    }
    }

     

    3. Model 설정

    목적 : 각 소셜 로그인의 리턴 필드가 다르기 때문에 추상화하여 interface, abstract class, class를 구현

     

    package io.security.oauth2.springsecurityoauth2.model;

    import org.springframework.security.core.GrantedAuthority;
    import org.springframework.security.oauth2.core.user.OAuth2User;

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

    public interface ProviderUser {

    String getId();

    String getUsername();

    String getPassword();

    String getEmail();

    String getProvider();

    String getPicture();

    List<? extends GrantedAuthority> getAuthorities();

    Map<String, Object> getAttributes();

    OAuth2User getOAuth2User();
    }

     

    소셜 인가서버(구글, 네이버, 카카오)의 공통만 정의한 클래스

     

    package io.security.oauth2.springsecurityoauth2.model;

    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.UUID;
    import java.util.stream.Collectors;

    public abstract class OAuth2ProviderUser implements ProviderUser {

    private OAuth2User oAuth2User;

    private ClientRegistration clientRegistration;

    Map<String, Object> attributes;

    public OAuth2ProviderUser(Map<String, Object> attributes, OAuth2User oAuth2User, ClientRegistration clientRegistration) {
    this.attributes = attributes;
    this.oAuth2User = oAuth2User;
    this.clientRegistration = clientRegistration;
    }


    @Override
    public String getPassword() { // 패스워드는 UUID로 설정
    return UUID.randomUUID().toString();
    }

    @Override
    public String getEmail() { // 이메일 정보 설정
    return (String) attributes.get("email");
    }

    @Override
    public String getProvider() { // 각 인가서버 아이디 설정
    return clientRegistration.getRegistrationId();
    }

    @Override
    public List<? extends GrantedAuthority> getAuthorities() { // 권한 부여 작업
    return oAuth2User.getAuthorities().stream()
    .map(authority -> new SimpleGrantedAuthority(authority.getAuthority()))
    .collect(Collectors.toList());
    }

    @Override
    public Map<String, Object> getAttributes() {
    return attributes; // 인가서버 리턴 정보에서 설정
    }

    @Override
    public OAuth2User getOAuth2User() {
    return oAuth2User; // 인증 객체 설정
    }
    }

     

    구글 인증 정보

     

    package io.security.oauth2.springsecurityoauth2.model;

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

    public class GoogleUser extends OAuth2ProviderUser{

    public GoogleUser(Attributes mainAttributes, OAuth2User oAuth2User, ClientRegistration clientRegistration) {
    super(mainAttributes.getMainAttributes(), oAuth2User, clientRegistration);
    }

    @Override
    public String getId() {
    // 식별자
    return (String) getAttributes().get("sub");
    }

    @Override
    public String getUsername() {
    // 실제 아이디
    return (String) getAttributes().get("sub");
    }

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

     

    네이버 인증 정보

     

    package io.security.oauth2.springsecurityoauth2.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 OAuth2ProviderUser{

    public NaverUser(Attributes attributes, OAuth2User oAuth2User, ClientRegistration clientRegistration) {
    // 네이버는 response 한 뎁스가 더 있음
    super(attributes.getSubAttributes(), oAuth2User, clientRegistration);
    }

    @Override
    public String getId() {
    // 식별자
    return (String) getAttributes().get("id");
    }

    @Override
    public String getUsername() {
    // 실제 아이디
    return (String) getAttributes().get("email");
    }

    @Override
    public String getPicture() {
    return (String) getAttributes().get("profile_image");
    }
    }

     

    카카오 인증 정보

     

    package io.security.oauth2.springsecurityoauth2.model;

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

    import java.util.Map;

    // kakao openid code와 다르다
    public class KakaoUser extends OAuth2ProviderUser{

    private Map<String, Object> otherAttributes;

    public KakaoUser(Attributes attributes, OAuth2User oAuth2User, ClientRegistration clientRegistration) {
    // 네이버는 response 한 뎁스가 더 있음
    super(attributes.getSubAttributes(), oAuth2User, clientRegistration);
    this.otherAttributes = attributes.getOtherAttributes();
    }

    @Override
    public String getId() {
    // 식별자
    return (String) getAttributes().get("id");
    }

    @Override
    public String getUsername() {
    // 실제 아이디
    return (String) otherAttributes.get("nickname");
    }

    @Override
    public String getPicture() {
    return (String) otherAttributes.get("profile_image_url");
    }

    }

     

    속성 interface

     

    package io.security.oauth2.springsecurityoauth2.model;


    import lombok.Builder;
    import lombok.Data;

    import java.util.Map;

    @Data
    @Builder
    public class Attributes {

    private Map<String, Object> mainAttributes;
    private Map<String, Object> subAttributes;
    private Map<String, Object> otherAttributes;
    }

     

    속성 필드 맵핑을 위한 클래스

     

    package io.security.oauth2.springsecurityoauth2.common.util;

    import io.security.oauth2.springsecurityoauth2.model.Attributes;
    import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;
    import org.springframework.security.oauth2.core.user.OAuth2User;

    import java.util.Map;

    public class OAuth2Utils {

    public static Attributes getMainAttributes(OAuth2User oAuth2User) { // 구글 1뎁스 정보

    return Attributes.builder()
    .mainAttributes(oAuth2User.getAttributes())
    .build();
    }

    public static Attributes getSubAttributes(OAuth2User oAuth2User, String subAttributesKey) { // 네이버 2뎁스정보

    Map<String, Object> subAttributes = (Map<String, Object>) oAuth2User.getAttributes().get(subAttributesKey);

    return Attributes.builder()
    .subAttributes(subAttributes)
    .build();
    }

    public static Attributes getOtherAttributes(OAuth2User oAuth2User, String subAttributesKey, String otherAttributesKey) { // 카카오 3뎁스 정보

    Map<String, Object> subAttributes = (Map<String, Object>) oAuth2User.getAttributes().get(subAttributesKey);
    Map<String, Object> otherAttributes = (Map<String, Object>) subAttributes.get(otherAttributesKey);

    return Attributes.builder()
    .subAttributes(subAttributes)
    .otherAttributes(otherAttributes)
    .build();
    }
    }

     

    기본 회원가입 유저, Authorization_code 유저 정보, openid connect 유저 정보 하나의 객체로 추상화

     

    package io.security.oauth2.springsecurityoauth2.model;

    import org.springframework.security.core.GrantedAuthority;
    import org.springframework.security.core.userdetails.UserDetails;
    import org.springframework.security.oauth2.core.oidc.OidcIdToken;
    import org.springframework.security.oauth2.core.oidc.OidcUserInfo;
    import org.springframework.security.oauth2.core.oidc.user.OidcUser;
    import org.springframework.security.oauth2.core.user.OAuth2User;

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

    public record PrincipalUser(ProviderUser providerUser) implements UserDetails, OidcUser, OAuth2User {

    @Override
    public String getName() {
    return providerUser.getUsername();
    }

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

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

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

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

    @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> getClaims() {
    return null;
    }

    @Override
    public OidcUserInfo getUserInfo() {
    return null;
    }

    @Override
    public OidcIdToken getIdToken() {
    return null;
    }
    }

     

    4. Repository 설정

    목적 : 유저정보 조회, 가입 (임시로 메모리에 저장)

     

    package io.security.oauth2.springsecurityoauth2.repository;


    import io.security.oauth2.springsecurityoauth2.entity.User;
    import org.springframework.stereotype.Repository;

    import java.util.HashMap;
    import java.util.Map;

    @Repository
    public class UserRepository {

    private Map<String, Object> users = new HashMap<>();

    public User findByUsername(String username) {
    if (users.containsKey(username)) {
    return (User) users.get(username);
    }
    return null;
    }

    public void register(User user) {
    if (users.containsKey(user.getUsername())) {
    return;
    }
    users.put(user.getUsername(), user);
    }
    }

     

    5. Service 설정

     

    목적 : UserDetailsService, OAuth2UserService(Authorization_code, Oidc 구분) 구현 - 유저정보 조회, 가입 서비스 구현 

     

    유저 회원가입 서비스

     

    package io.security.oauth2.springsecurityoauth2.service;

    import io.security.oauth2.springsecurityoauth2.entity.User;
    import io.security.oauth2.springsecurityoauth2.model.ProviderUser;
    import io.security.oauth2.springsecurityoauth2.repository.UserRepository;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;

    @Service
    public class UserService {

    @Autowired
    private UserRepository userRepository;

    public void register(String registrationId, ProviderUser providerUser) {

    User.builder()
    .id(providerUser.getId())
    .registrationId(registrationId)
    .username(providerUser.getUsername())
    .provider(providerUser.getProvider())
    .email(providerUser.getEmail())
    .authorities(providerUser.getAuthorities())
    .build();
    }
    }

     

    유저정보클래스 변환용 interface

     

    package io.security.oauth2.springsecurityoauth2.common.converters;

    public interface ProviderUserConverter<T,R> {

    R converter(T t);

    }

     

    유저 정보 컨버팅용 추상 클래스 (Authorization_code, Oidc 클래스들의 추상화용)

     

    package io.security.oauth2.springsecurityoauth2.service;


    import io.security.oauth2.springsecurityoauth2.common.converters.ProviderUserConverter;
    import io.security.oauth2.springsecurityoauth2.common.converters.ProviderUserRequest;
    import io.security.oauth2.springsecurityoauth2.entity.User;
    import io.security.oauth2.springsecurityoauth2.model.*;
    import io.security.oauth2.springsecurityoauth2.repository.UserRepository;
    import lombok.Getter;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest;
    import org.springframework.stereotype.Service;

    @Service
    @Getter
    public class AbstractOAuth2UserService {

    @Autowired
    private UserService userService;

    @Autowired
    private UserRepository userRepository;

    @Autowired
    private ProviderUserConverter<ProviderUserRequest, ProviderUser> providerUserConverter;

    protected ProviderUser providerUser(ProviderUserRequest providerUserRequest) {

    return providerUserConverter.converter(providerUserRequest);
    }

    protected void register(ProviderUser providerUser, OAuth2UserRequest userRequest) {

    User user = userRepository.findByUsername(providerUser.getUsername());

    if (user == null) {
    userService.register(userRequest.getClientRegistration().getRegistrationId(), providerUser);
    } else {
    System.out.println("user = " + user);
    }

    }
    }

     

    시큐리티 유저정보 조회 클래스인 loadUserByUsername 임시 구현

     

    package io.security.oauth2.springsecurityoauth2.service;

    import io.security.oauth2.springsecurityoauth2.common.converters.ProviderUserRequest;
    import io.security.oauth2.springsecurityoauth2.entity.User;
    import io.security.oauth2.springsecurityoauth2.model.PrincipalUser;
    import io.security.oauth2.springsecurityoauth2.model.ProviderUser;
    import io.security.oauth2.springsecurityoauth2.repository.UserRepository;
    import lombok.RequiredArgsConstructor;
    import org.springframework.security.core.authority.AuthorityUtils;
    import org.springframework.security.core.userdetails.UserDetails;
    import org.springframework.security.core.userdetails.UserDetailsService;
    import org.springframework.security.core.userdetails.UsernameNotFoundException;
    import org.springframework.stereotype.Service;

    @Service
    @RequiredArgsConstructor
    public class CustomUserDetailsService extends AbstractOAuth2UserService implements UserDetailsService {

    private final UserRepository userRepository;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {

    User user = userRepository.findByUsername(username);

    if (user == null) {
    user = User.builder()
    .id("1")
    .username("user1")
    .password("{noop}1234")
    .authorities(AuthorityUtils.createAuthorityList("ROLE_USER"))
    .email("user@a.com")
    .build();
    }

    ProviderUserRequest providerUserRequest = new ProviderUserRequest(user);
    ProviderUser providerUser = providerUser(providerUserRequest);

    return new PrincipalUser(providerUser);
    }
    }

     

    Athorization_code 방식 서비스

     

    package io.security.oauth2.springsecurityoauth2.service;


    import io.security.oauth2.springsecurityoauth2.common.converters.ProviderUserRequest;
    import io.security.oauth2.springsecurityoauth2.model.PrincipalUser;
    import io.security.oauth2.springsecurityoauth2.model.ProviderUser;
    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.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> {

    @Override // 새로 객체 리턴하기 위해 오버라이딩
    public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException {

    ClientRegistration clientRegistration = userRequest.getClientRegistration(); // 인가서버 아이디

    OAuth2UserService<OAuth2UserRequest, OAuth2User> oAuth2UserService = new DefaultOAuth2UserService(); // 기존 시큐리티 구현 정보

    OAuth2User oAuth2User = oAuth2UserService.loadUser(userRequest); // 인증 정보 생성

    ProviderUserRequest providerUserRequest = new ProviderUserRequest(clientRegistration, oAuth2User); // 정보 설정

    ProviderUser providerUser = providerUser(providerUserRequest); // 인가 서버 아이디에 맞게 정보 설정

    // 회원가입

    super.register(providerUser, userRequest);

    return new PrincipalUser(providerUser);
    }
    }

     

    openid connect 방식 서비스

     

    package io.security.oauth2.springsecurityoauth2.service;

    import io.security.oauth2.springsecurityoauth2.common.converters.ProviderUserRequest;
    import io.security.oauth2.springsecurityoauth2.model.PrincipalUser;
    import io.security.oauth2.springsecurityoauth2.model.ProviderUser;
    import org.springframework.security.oauth2.client.oidc.userinfo.OidcUserRequest;
    import org.springframework.security.oauth2.client.oidc.userinfo.OidcUserService;
    import org.springframework.security.oauth2.client.registration.ClientRegistration;
    import org.springframework.security.oauth2.client.userinfo.OAuth2UserService;
    import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
    import org.springframework.security.oauth2.core.oidc.user.OidcUser;
    import org.springframework.stereotype.Service;

    @Service
    public class CustomOidcUserService extends AbstractOAuth2UserService implements OAuth2UserService<OidcUserRequest, OidcUser> {

    @Override // 새로 객체 리턴하기 위해 오버라이딩
    public OidcUser loadUser(OidcUserRequest userRequest) throws OAuth2AuthenticationException {

    ClientRegistration clientRegistration = userRequest.getClientRegistration(); // 인가서버 아이디

    OAuth2UserService<OidcUserRequest, OidcUser> oidcUserService = new OidcUserService(); // 기존 시큐리티 구현정보

    OidcUser oidcUser = oidcUserService.loadUser(userRequest); // 유저 정보 생성

    ProviderUserRequest providerUserRequest = new ProviderUserRequest(clientRegistration, oidcUser); // 정보 설정

    ProviderUser providerUser = providerUser(providerUserRequest); // 인가 서버 아이디에 맞게 정보 설정

    // 회원가입

    super.register(providerUser, userRequest);

    return new PrincipalUser(providerUser); // 공통 객체 리턴
    }
    }

     

    6. Converter 설정

     

    목적 : 각 소셜 인가서버의 타입에 맞게 인증 객체 생성 후 공통 클래스로 변환

     

    타입 변환용 인터페이스

     

    package io.security.oauth2.springsecurityoauth2.common.converters;

    public interface ProviderUserConverter<T,R> {

    R converter(T t);

    }

     

    각 인가서버 Enum 정보

     

    package io.security.oauth2.springsecurityoauth2.common.enums;

    public class OAuth2Config {

    public enum SocialType {
    GOOGLE("google"),
    NAVER("naver"),
    KAKAO("kakao"),
    ;

    private final String socialName;

    SocialType(String socialName) {
    this.socialName = socialName;
    }

    public String getSocialName() {
    return socialName;
    }

    }

    }

     

    구글 컨버터 

     

    package io.security.oauth2.springsecurityoauth2.common.converters;

    import io.security.oauth2.springsecurityoauth2.common.enums.OAuth2Config;
    import io.security.oauth2.springsecurityoauth2.model.GoogleUser;
    import io.security.oauth2.springsecurityoauth2.model.ProviderUser;
    import io.security.oauth2.springsecurityoauth2.common.util.OAuth2Utils;

    public class OAuth2GoogleProviderUserConverter implements ProviderUserConverter<ProviderUserRequest, ProviderUser> {

    @Override
    public ProviderUser converter(ProviderUserRequest providerUserRequest) {

    if (!providerUserRequest.clientRegistration().getRegistrationId().equals(OAuth2Config.SocialType.GOOGLE.getSocialName())) {
    return null;
    }
    return new GoogleUser(
    OAuth2Utils.getMainAttributes(providerUserRequest.oAuth2User()),
    providerUserRequest.oAuth2User(),
    providerUserRequest.clientRegistration());
    }


    }

     

    카카오 컨버터

     

    package io.security.oauth2.springsecurityoauth2.common.converters;

    import io.security.oauth2.springsecurityoauth2.common.enums.OAuth2Config;
    import io.security.oauth2.springsecurityoauth2.common.util.OAuth2Utils;
    import io.security.oauth2.springsecurityoauth2.model.KakaoUser;
    import io.security.oauth2.springsecurityoauth2.model.NaverUser;
    import io.security.oauth2.springsecurityoauth2.model.ProviderUser;

    public class OAuth2KakaoProviderUserConverter implements ProviderUserConverter<ProviderUserRequest, ProviderUser> {
    @Override
    public ProviderUser converter(ProviderUserRequest providerUserRequest) {

    if (!providerUserRequest.clientRegistration().getRegistrationId().equals(OAuth2Config.SocialType.KAKAO.getSocialName())) {
    return null;
    }
    return new KakaoUser(
    OAuth2Utils.getOtherAttributes(providerUserRequest.oAuth2User(), "kakao_account", "profile"),
    providerUserRequest.oAuth2User(),
    providerUserRequest.clientRegistration());
    }
    }

     

    네이버 컨버터

     

    package io.security.oauth2.springsecurityoauth2.common.converters;

    import io.security.oauth2.springsecurityoauth2.common.enums.OAuth2Config;
    import io.security.oauth2.springsecurityoauth2.model.NaverUser;
    import io.security.oauth2.springsecurityoauth2.model.ProviderUser;
    import io.security.oauth2.springsecurityoauth2.common.util.OAuth2Utils;

    public class OAuth2NaverProviderUserConverter implements ProviderUserConverter<ProviderUserRequest, ProviderUser> {
    @Override
    public ProviderUser converter(ProviderUserRequest providerUserRequest) {

    if (!providerUserRequest.clientRegistration().getRegistrationId().equals(OAuth2Config.SocialType.NAVER.getSocialName())) {
    return null;
    }
    return new NaverUser(
    OAuth2Utils.getSubAttributes(providerUserRequest.oAuth2User(), "response"),
    providerUserRequest.oAuth2User(),
    providerUserRequest.clientRegistration());
    }
    }

     

    일반 유저 컨터버

     

    package io.security.oauth2.springsecurityoauth2.common.converters;

    import io.security.oauth2.springsecurityoauth2.common.enums.OAuth2Config;
    import io.security.oauth2.springsecurityoauth2.common.util.OAuth2Utils;
    import io.security.oauth2.springsecurityoauth2.entity.User;
    import io.security.oauth2.springsecurityoauth2.model.FormUser;
    import io.security.oauth2.springsecurityoauth2.model.NaverUser;
    import io.security.oauth2.springsecurityoauth2.model.ProviderUser;

    public class UserDetailsProviderUserConverter implements ProviderUserConverter<ProviderUserRequest, ProviderUser> {
    @Override
    public ProviderUser converter(ProviderUserRequest providerUserRequest) {

    if (providerUserRequest.user() == null) {
    return null;
    }


    User user = providerUserRequest.user();
    return FormUser.builder()
    .id(user.getId())
    .username(user.getUsername())
    .password(user.getPassword())
    .email(user.getEmail())
    .authorities(user.getAuthorities())
    .provider(user.getProvider())
    .build();
    }
    }

     

    유저 컨버팅 DTO

     

    package io.security.oauth2.springsecurityoauth2.common.converters;

    import io.security.oauth2.springsecurityoauth2.entity.User;
    import org.springframework.security.oauth2.client.registration.ClientRegistration;
    import org.springframework.security.oauth2.core.user.OAuth2User;

    // record는 새로 추가된 자바 기능에서 기본적인 롬복의 getter tostring,hash등을 기본 지원
    // 멤버변수는 private final로 선언된다
    // 필드별 getter가 자동으로 생성된다
    // 모든 멤버변수를 인수로 가지는 생성자를 자동으로 생성해 준다
    // getter, equals, hashcode, toString과 같은 기본 메소드를 제공한다
    // 컴팩트 생성자 : 클래스 생성자와 달리 레코드 생성자에는 공식적인 매개변수 목록이 없으며 이를 컴팩트 생성자라고 한다.
    // 레코드는 암시적으로 fianl이며 다른 클래스를 상속받을수 없다.
    public record ProviderUserRequest(ClientRegistration clientRegistration, OAuth2User oAuth2User, User user) {

    public ProviderUserRequest(ClientRegistration clientRegistration, OAuth2User oAuth2User) {
    this(clientRegistration, oAuth2User, null);
    }

    public ProviderUserRequest(User user) {
    this(null, null, user);
    }
    }

     

    컨버터 위임 클래스

     

    package io.security.oauth2.springsecurityoauth2.common.converters;

    import io.security.oauth2.springsecurityoauth2.model.ProviderUser;
    import org.springframework.stereotype.Component;
    import org.springframework.util.Assert;

    import java.util.Arrays;
    import java.util.Collections;
    import java.util.LinkedList;
    import java.util.List;

    @Component
    public class DelegatingProviderUserConverter implements ProviderUserConverter<ProviderUserRequest, ProviderUser> {

    private List<ProviderUserConverter<ProviderUserRequest, ProviderUser>> converters;

    public DelegatingProviderUserConverter() {
    List<ProviderUserConverter<ProviderUserRequest, ProviderUser>> providerUserConverters
    = Arrays.asList(
    new UserDetailsProviderUserConverter(),
    new OAuth2GoogleProviderUserConverter(),
    new OAuth2NaverProviderUserConverter(),
    new OAuth2KakaoProviderUserConverter());

    this.converters = Collections.unmodifiableList(new LinkedList<>(providerUserConverters));
    }

    @Override
    public ProviderUser converter(ProviderUserRequest providerUserRequest) {

    Assert.notNull(providerUserRequest, "providerUserRequest cannot be null");

    for (ProviderUserConverter<ProviderUserRequest, ProviderUser> converter : converters) {

    ProviderUser providerUser = converter.converter(providerUserRequest);

    if (providerUser != null) return providerUser;

    }
    return null;
    }
    }

     

    github ( branch : 스프링시큐리티OAuth2.0_Client ) : https://github.com/JEONSEUNGREE/Oauth2-SpringBoot

     

    GitHub - JEONSEUNGREE/Oauth2-SpringBoot: Oauth2

    Oauth2. Contribute to JEONSEUNGREE/Oauth2-SpringBoot development by creating an account on GitHub.

    github.com

     

    학습 강의 : https://www.inflearn.com/course/%EC%A0%95%EC%88%98%EC%9B%90-%EC%8A%A4%ED%94%84%EB%A7%81-%EC%8B%9C%ED%81%90%EB%A6%AC%ED%8B%B0/dashboard

     

    스프링 시큐리티 OAuth2 - Spring Boot 기반으로 개발하는 Spring Security OAuth2 - 인프런 | 강의

    스프링 시큐리티 OAuth2의 기본 개념부터 API 사용법과 내부 아키텍처를 학습합니다. 아울러 OAuth2 Client, OAuth2 Resource Server, Authorization Server를 통합하여 연동하는 방법을 살펴보고 자체 인가 서버를

    www.inflearn.com

     

    댓글

Designed by Tistory.