-
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
'BackEnd > SpringSecurity' 카테고리의 다른 글
SpringSecurity OAuth2.0 - 2 (0) 2023.07.09 SpringSecurity OAuth2.0 - 1 (0) 2023.07.08 KeyCloak으로 Oauth2.0 인가서버 구현 - 2 (0) 2023.07.08 KeyCloak으로 Oauth2.0 인가서버 구현 - 1 (0) 2023.07.08