Skip to content

Commit

Permalink
refactor: Add Kakao Oauth2 and refactor some for #3
Browse files Browse the repository at this point in the history
  • Loading branch information
HanaHww2 committed Oct 16, 2022
1 parent 91c5d9f commit fa6cf9a
Show file tree
Hide file tree
Showing 35 changed files with 276 additions and 198 deletions.
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package me.study.mylog.auth.properties;
package me.study.mylog.auth.config;

import lombok.Getter;
import lombok.Setter;
Expand All @@ -25,13 +25,13 @@ public static class OAuth2 {

private List<String> authorizedRedirectUris = new ArrayList<>();

public List<String> getAuthorizedRedirectUris() {
return authorizedRedirectUris;
}
public OAuth2 authorizedRedirectUris(List<String> authorizedRedirectUris) {
this.authorizedRedirectUris = authorizedRedirectUris;
return this;
}
public List<String> getAuthorizedRedirectUris() {
return authorizedRedirectUris;
}
}

public Token getToken() {
Expand Down
18 changes: 6 additions & 12 deletions src/main/java/me/study/mylog/auth/config/SecurityConfig.java
Original file line number Diff line number Diff line change
@@ -1,13 +1,7 @@
package me.study.mylog.auth.config;

import lombok.RequiredArgsConstructor;
import me.study.mylog.auth.handler.JwtAccessDeniedHandler;
import me.study.mylog.auth.handler.JwtAuthenticationEntryPoint;
import me.study.mylog.auth.handler.OAuth2AuthenticationFailureHandler;
import me.study.mylog.auth.handler.OAuth2AuthenticationSuccessHandler;
import me.study.mylog.auth.security.CustomOAuth2UserService;
import me.study.mylog.auth.security.JwtAuthenticationFilter;
import me.study.mylog.auth.security.OAuth2CookieAuthorizationRequestRepository;
import me.study.mylog.auth.security.*;
import me.study.mylog.users.domain.RoleType;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
Expand All @@ -25,9 +19,9 @@
@Configuration
public class SecurityConfig {

//private final CustomOAuth2UserService customOAuth2UserService;
private final JwtAuthenticationFilter jwtAuthenticationFilter;
private final CustomOAuth2UserService customOAuth2UserService;
// private final OAuth2CookieAuthorizationRequestRepository oAuth2CookieAuthorizationRequestRepository;
private final OAuth2CookieAuthorizationRequestRepository oAuth2CookieAuthorizationRequestRepository;
private final OAuth2AuthenticationSuccessHandler oAuth2AuthenticationSuccessHandler;
private final OAuth2AuthenticationFailureHandler oAuth2AuthenticationFailureHandler;
private final JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;
Expand Down Expand Up @@ -73,13 +67,13 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
)
.oauth2Login()
.authorizationEndpoint()
// .authorizationRequestRepository(oAuth2CookieAuthorizationRequestRepository)
.authorizationRequestRepository(oAuth2CookieAuthorizationRequestRepository)
.and()
.userInfoEndpoint()
.userService(customOAuth2UserService)
//.userService(customOAuth2UserService)
.and()
.successHandler(oAuth2AuthenticationSuccessHandler)
.failureHandler(oAuth2AuthenticationFailureHandler)
// .failureHandler(oAuth2AuthenticationFailureHandler)
.and()
.exceptionHandling()
.authenticationEntryPoint(jwtAuthenticationEntryPoint) // 401
Expand Down
35 changes: 35 additions & 0 deletions src/main/java/me/study/mylog/auth/oauth/GoogleOAuth2UserInfo.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package me.study.mylog.auth.oauth;

import me.study.mylog.users.domain.AuthProviderType;
import java.util.Map;

public class GoogleOAuth2UserInfo extends OAuth2UserInfo {

private static final AuthProviderType authProviderType = AuthProviderType.GOOGLE;

public GoogleOAuth2UserInfo(Map<String, Object> attributes) {
super(attributes);
}

@Override
public AuthProviderType getProviderType() {
return authProviderType;
}
@Override
public String getId() {
return (String) attributes.get("sub");
}
@Override
public String getName() {
return (String) attributes.get("name");
}
@Override
public String getEmail() {
return (String) attributes.get("email");
}
@Override
public String getImageUrl() {
return (String) attributes.get("picture");
}

}
53 changes: 53 additions & 0 deletions src/main/java/me/study/mylog/auth/oauth/KakaoOAuth2UserInfo.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package me.study.mylog.auth.oauth;

import me.study.mylog.users.domain.AuthProviderType;
import java.util.Map;

public class KakaoOAuth2UserInfo extends OAuth2UserInfo {

private static final AuthProviderType authProviderType = AuthProviderType.KAKAO;
private Map<String, Object> attributesAccount;
private Map<String, Object> attributesProfile;

public KakaoOAuth2UserInfo(Map<String, Object> attributes) {
super(attributes);
// 카카오 응답값 기준 파싱
this.attributesAccount = (Map<String, Object>) attributes.get("kakao_account");
this.attributesProfile = (Map<String, Object>) attributesAccount.get("profile");
}

@Override
public AuthProviderType getProviderType() {
return authProviderType;
}

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

@Override
public String getEmail() {
if (attributesAccount == null) {
return null;
}
return (String) attributesAccount.get("email");
}

@Override
public String getName() {
if (attributesProfile == null) {
return null;
}
return (String) attributesProfile.get("nickname");
}

@Override
public String getImageUrl() {
if (attributesProfile == null) {
return null;
}
return (String) attributesProfile.get("profile_image");
}

}
88 changes: 35 additions & 53 deletions src/main/java/me/study/mylog/auth/oauth/OAuth2UserInfo.java
Original file line number Diff line number Diff line change
@@ -1,62 +1,44 @@
package me.study.mylog.auth.oauth;

import lombok.Builder;
import me.study.mylog.users.domain.AuthProviderType;
import me.study.mylog.users.domain.RoleType;
import me.study.mylog.users.domain.User;
import me.study.mylog.users.domain.UserStatus;

import java.util.Map;
import java.util.UUID;

public class OAuth2UserInfo {

Map<String, Object> attributes;
String nameAttributeKey;
String id;
String name;
String email;
String imageUrl;

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

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

public String getId () { return id; }

public String getName() { return name; }

public String getEmail() { return email; }

public String getImageUrl() { return imageUrl; }

@Builder
public OAuth2UserInfo(Map<String, Object> attributes, String nameAttributeKey, String name,
String email, String imageUrl) {
this.attributes = attributes;
this.nameAttributeKey= nameAttributeKey;
this.name = name;
this.email = email;
this.imageUrl = imageUrl;
}

public User toEntity() {
return User.builder()
.status(UserStatus.Normal)
.name(name+ UUID.randomUUID()) // TODO 해당 속성은 유니크이므로, 선검증이 추가로 필요하다.
.nickname(name)
.email(email)
.imageUrl(imageUrl)
.role(RoleType.USER)
.status(UserStatus.Normal)
.build();
}

// /* 구현체에 객체 생성 처리를 맡긴다. 팩토리 패턴이라고 볼 수 있나? */
// public abstract void createUserInfo(AuthProvider authProvider);
// public OAuth2UserInfo(AuthProvider authProvider, Map<String, Object> attributes) {
// this.attributes = attributes;
// createUserInfo(authProvider);
// }
}
public abstract class OAuth2UserInfo {
Map<String, Object> attributes;

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

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

public abstract AuthProviderType getProviderType();

public abstract String getId();

public abstract String getName();

public abstract String getEmail();

public abstract String getImageUrl();

public User toEntity(String tempName) {

return User.builder()
.name(tempName) // TODO 해당 속성은 유니크이므로, 선검증이 추가로 필요하다.
.nickname(getName())
.email(getEmail())
.imageUrl(getImageUrl())
.role(RoleType.USER)
.status(UserStatus.Normal)
.authProviderType(getProviderType())
.build();
}
}
13 changes: 2 additions & 11 deletions src/main/java/me/study/mylog/auth/oauth/OAuth2UserInfoFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,10 @@ public class OAuth2UserInfoFactory {

public static OAuth2UserInfo getOAuth2UserInfo(AuthProviderType authProvider, Map<String, Object> attributes) {
switch (authProvider) {
case GOOGLE: return ofGoogle(attributes);
case GOOGLE: return new GoogleOAuth2UserInfo(attributes);
case KAKAO: return new KakaoOAuth2UserInfo(attributes);
//case GITHUB: return new GithubOAuth2UserInfo(attributes);
default: throw new IllegalArgumentException("Invalid Provider Type.");
}
}

private static OAuth2UserInfo ofGoogle(Map<String, Object> attributes) {
return OAuth2UserInfo.builder()
.name((String) attributes.get("name"))
.email((String) attributes.get("email"))
.imageUrl((String) attributes.get("picture"))
.attributes(attributes)
.build();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -8,55 +8,62 @@
import me.study.mylog.users.domain.AuthProviderType;
import me.study.mylog.users.domain.User;
import me.study.mylog.users.repository.UserRepository;
import me.study.mylog.users.service.UserService;
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;
import org.springframework.transaction.annotation.Transactional;

import java.util.Optional;

@RequiredArgsConstructor
@Slf4j
@Service
public class CustomOAuth2UserService implements OAuth2UserService<OAuth2UserRequest, OAuth2User> {

private final UserRepository userRepository;
private final UserService userService;

// OAuth2UserRequest에 있는 Access Token으로 유저정보 get
@Override
public OAuth2User loadUser(OAuth2UserRequest oAuth2UserRequest) throws OAuth2AuthenticationException {
DefaultOAuth2UserService delegate = new DefaultOAuth2UserService();
// 써드파티에 OAuth2UserRequest 를 보내고 받은 응답값에 있는 Access Token으로 유저정보 get
OAuth2User oAuth2User = delegate.loadUser(oAuth2UserRequest);

return process(oAuth2UserRequest, oAuth2User);
}

// TODO readonly 트랜잭션 전파 체크해보기 (https://code-mania.tistory.com/m/143)
// 획득한 유저정보를 Java Model과 매핑하고 프로세스 진행
private OAuth2User process(OAuth2UserRequest oAuth2UserRequest, OAuth2User oAuth2User) {
@Transactional
OAuth2User process(OAuth2UserRequest oAuth2UserRequest, OAuth2User oAuth2User) {

AuthProviderType authProviderType = AuthProviderType.valueOf(oAuth2UserRequest.getClientRegistration().getRegistrationId().toUpperCase());

// 이왕 팩토리패턴을 적용하니, 인터페이스나 추상 클래스를 사용하는 게 나을 것 같다.
// 이왕 팩토리패턴을 적용하니, 인터페이스를 사용
OAuth2UserInfo userInfo = OAuth2UserInfoFactory.getOAuth2UserInfo(authProviderType, oAuth2User.getAttributes());

if (userInfo.getEmail().isEmpty()) {
throw new OAuthProcessingException("Email not found from OAuth2 provider");
}

// 유저 정보 조회
Optional<User> userOptional = userRepository.findByEmail(userInfo.getEmail());
User user;

if (userOptional.isPresent()) { // 이미 가입된 경우
user = userOptional.get();
if (authProviderType != user.getAuthProviderType()) {
if (authProviderType.equals(user.getAuthProviderType())) {
throw new OAuthProcessingException("Wrong Match Auth Provider");
}
} else { // 가입되지 않은 경우
user = userInfo.toEntity();
userRepository.save(user);
user = userService.registerForOauth2(userInfo);
}
return UserPrincipal.create(user, oAuth2User.getAttributes());
}


}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package me.study.mylog.auth.handler;
package me.study.mylog.auth.security;

import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.access.AccessDeniedHandler;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package me.study.mylog.auth.handler;
package me.study.mylog.auth.security;

import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
Expand Down
Loading

0 comments on commit fa6cf9a

Please sign in to comment.