From c9eb3c0a0f7cbdd8e085a71852528cde0e7ca621 Mon Sep 17 00:00:00 2001 From: Ahn Su-gyeong Date: Sat, 1 Jun 2024 05:35:51 +0900 Subject: [PATCH] =?UTF-8?q?:sparkles:=20Feature:=20=EA=B5=AC=EA=B8=80=20?= =?UTF-8?q?=EB=A1=9C=EA=B7=B8=EC=9D=B8=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84=20(#92)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../zzangdol/auth/implement/AuthService.java | 10 ++- .../zzangdol/user/domain/AuthProvider.java | 2 +- .../main/java/zzangdol/user/domain/User.java | 2 + .../client/google/GoogleLoginClient.java | 21 ++++++ .../client/google/GoogleLoginTokenClient.java | 68 +++++++++++++++++++ .../client/google/GoogleLoginUserClient.java | 31 +++++++++ .../client/{ => kakao}/KakaoLoginClient.java | 3 +- .../{ => kakao}/KakaoLoginTokenClient.java | 2 +- .../{ => kakao}/KakaoLoginUserClient.java | 2 +- .../oauth/dto/GoogleTokenResponse.java | 22 ++++++ .../oauth/dto/GoogleUserInfoResponse.java | 20 ++++++ 11 files changed, 177 insertions(+), 6 deletions(-) create mode 100644 moodoodle-infrastructure/src/main/java/zzangdol/oauth/client/google/GoogleLoginClient.java create mode 100644 moodoodle-infrastructure/src/main/java/zzangdol/oauth/client/google/GoogleLoginTokenClient.java create mode 100644 moodoodle-infrastructure/src/main/java/zzangdol/oauth/client/google/GoogleLoginUserClient.java rename moodoodle-infrastructure/src/main/java/zzangdol/oauth/client/{ => kakao}/KakaoLoginClient.java (89%) rename moodoodle-infrastructure/src/main/java/zzangdol/oauth/client/{ => kakao}/KakaoLoginTokenClient.java (98%) rename moodoodle-infrastructure/src/main/java/zzangdol/oauth/client/{ => kakao}/KakaoLoginUserClient.java (95%) create mode 100644 moodoodle-infrastructure/src/main/java/zzangdol/oauth/dto/GoogleTokenResponse.java create mode 100644 moodoodle-infrastructure/src/main/java/zzangdol/oauth/dto/GoogleUserInfoResponse.java diff --git a/moodoodle-api/src/main/java/zzangdol/auth/implement/AuthService.java b/moodoodle-api/src/main/java/zzangdol/auth/implement/AuthService.java index 55eda2c..a3953b7 100644 --- a/moodoodle-api/src/main/java/zzangdol/auth/implement/AuthService.java +++ b/moodoodle-api/src/main/java/zzangdol/auth/implement/AuthService.java @@ -15,6 +15,7 @@ import zzangdol.jwt.JwtResponse; import zzangdol.jwt.JwtService; import zzangdol.oauth.client.SocialLoginClient; +import zzangdol.oauth.dto.GoogleUserInfoResponse; import zzangdol.oauth.dto.KakaoUserInfoResponse; import zzangdol.oauth.dto.SocialUserInfoResponse; import zzangdol.response.status.ErrorStatus; @@ -35,13 +36,18 @@ public class AuthService { private static final int RANDOM_PASSWORD_LENGTH = 10; - public AuthService(PasswordEncoder passwordEncoder, UserRepository userRepository, JwtService jwtService, - SocialLoginClient kakaoLoginClient /*, SocialLoginClient naverLoginClient */) { + public AuthService(PasswordEncoder passwordEncoder, + UserRepository userRepository, + JwtService jwtService, + SocialLoginClient kakaoLoginClient, + SocialLoginClient googleLoginClient + /*, SocialLoginClient naverLoginClient */) { this.passwordEncoder = passwordEncoder; this.userRepository = userRepository; this.jwtService = jwtService; this.socialLoginClients = new EnumMap<>(AuthProvider.class); this.socialLoginClients.put(AuthProvider.KAKAO, kakaoLoginClient); + this.socialLoginClients.put(AuthProvider.GOOGLE, googleLoginClient); // this.socialLoginClients.put(AuthProvider.NAVER, naverLoginClient); } diff --git a/moodoodle-domain/src/main/java/zzangdol/user/domain/AuthProvider.java b/moodoodle-domain/src/main/java/zzangdol/user/domain/AuthProvider.java index bfdea02..151c9e4 100644 --- a/moodoodle-domain/src/main/java/zzangdol/user/domain/AuthProvider.java +++ b/moodoodle-domain/src/main/java/zzangdol/user/domain/AuthProvider.java @@ -1,5 +1,5 @@ package zzangdol.user.domain; public enum AuthProvider { - KAKAO, NAVER, APPLE, DEFAULT + KAKAO, GOOGLE, NAVER, APPLE, DEFAULT } diff --git a/moodoodle-domain/src/main/java/zzangdol/user/domain/User.java b/moodoodle-domain/src/main/java/zzangdol/user/domain/User.java index f1f5e2d..206ef54 100644 --- a/moodoodle-domain/src/main/java/zzangdol/user/domain/User.java +++ b/moodoodle-domain/src/main/java/zzangdol/user/domain/User.java @@ -1,5 +1,6 @@ package zzangdol.user.domain; +import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.EnumType; import jakarta.persistence.Enumerated; @@ -33,6 +34,7 @@ public class User extends BaseTimeEntity implements UserDetails { private LocalTime notificationTime; private Boolean isRead; + @Column(nullable = false, length = 50) @Enumerated(EnumType.STRING) private AuthProvider authProvider; diff --git a/moodoodle-infrastructure/src/main/java/zzangdol/oauth/client/google/GoogleLoginClient.java b/moodoodle-infrastructure/src/main/java/zzangdol/oauth/client/google/GoogleLoginClient.java new file mode 100644 index 0000000..26462b9 --- /dev/null +++ b/moodoodle-infrastructure/src/main/java/zzangdol/oauth/client/google/GoogleLoginClient.java @@ -0,0 +1,21 @@ +package zzangdol.oauth.client.google; + +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import zzangdol.oauth.client.SocialLoginClient; +import zzangdol.oauth.dto.GoogleTokenResponse; +import zzangdol.oauth.dto.GoogleUserInfoResponse; + +@RequiredArgsConstructor +@Service +public class GoogleLoginClient implements SocialLoginClient { + + private final GoogleLoginTokenClient googleLoginTokenClient; + private final GoogleLoginUserClient googleLoginUserClient; + + @Override + public GoogleUserInfoResponse getUserInfo(String authorizationCode) { + GoogleTokenResponse tokenInfo = googleLoginTokenClient.getTokenInfo(authorizationCode); + return googleLoginUserClient.getUserInfo(tokenInfo.getAccessToken()); + } +} diff --git a/moodoodle-infrastructure/src/main/java/zzangdol/oauth/client/google/GoogleLoginTokenClient.java b/moodoodle-infrastructure/src/main/java/zzangdol/oauth/client/google/GoogleLoginTokenClient.java new file mode 100644 index 0000000..ea2d58d --- /dev/null +++ b/moodoodle-infrastructure/src/main/java/zzangdol/oauth/client/google/GoogleLoginTokenClient.java @@ -0,0 +1,68 @@ +package zzangdol.oauth.client.google; + +import jakarta.annotation.PostConstruct; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.MediaType; +import org.springframework.stereotype.Component; +import org.springframework.web.reactive.function.BodyInserters; +import org.springframework.web.reactive.function.client.WebClient; +import reactor.core.publisher.Mono; +import zzangdol.oauth.dto.GoogleTokenResponse; + +@Slf4j +@Component +public class GoogleLoginTokenClient { + + private WebClient webClient; + + @Value("${google.token.uri}") + private String TOKEN_URI; + + @Value("${google.redirect.uri}") + private String REDIRECT_URI; + + @Value("${google.grant.type}") + private String GRANT_TYPE; + + @Value("${google.client.id}") + private String CLIENT_ID; + + @Value("${google.client.secret}") + private String CLIENT_SECRET; + + @Value("${google.scope}") + private String SCOPE; + + @Value("${google.response_type}") + private String RESPONSE_TYPE; + + @PostConstruct + private void init() { + this.webClient = WebClient.builder() + .baseUrl(TOKEN_URI) + .build(); + } + + public GoogleTokenResponse getTokenInfo(final String code) { + log.info("Requesting token with code: {}", code); + + return webClient.post() + .uri("") + .contentType(MediaType.APPLICATION_FORM_URLENCODED) + .body(BodyInserters.fromFormData("grant_type", GRANT_TYPE) + .with("client_id", CLIENT_ID) + .with("redirect_uri", REDIRECT_URI) + .with("code", code) + .with("client_secret", CLIENT_SECRET)) + .retrieve() + .onStatus(status -> status.is4xxClientError() || status.is5xxServerError(), clientResponse -> clientResponse.bodyToMono(String.class) + .flatMap(response -> { + log.error("Error Response: {}", response); + return Mono.error(new RuntimeException("Error Response: " + response)); + })) + .bodyToMono(GoogleTokenResponse.class) + .block(); + } + +} diff --git a/moodoodle-infrastructure/src/main/java/zzangdol/oauth/client/google/GoogleLoginUserClient.java b/moodoodle-infrastructure/src/main/java/zzangdol/oauth/client/google/GoogleLoginUserClient.java new file mode 100644 index 0000000..f4e2ebf --- /dev/null +++ b/moodoodle-infrastructure/src/main/java/zzangdol/oauth/client/google/GoogleLoginUserClient.java @@ -0,0 +1,31 @@ +package zzangdol.oauth.client.google; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; +import org.springframework.web.reactive.function.client.WebClient; +import zzangdol.oauth.dto.GoogleUserInfoResponse; + +@Slf4j +@Component +public class GoogleLoginUserClient { + + private final WebClient webClient; + + private static final String USER_INFO_URI = "https://www.googleapis.com/oauth2/v3/userinfo"; + + public GoogleLoginUserClient() { + this.webClient = WebClient.builder() + .baseUrl(USER_INFO_URI) + .build(); + } + + public GoogleUserInfoResponse getUserInfo(final String token) { + return webClient.get() + .uri("") + .header("Authorization", "Bearer " + token) + .retrieve() + .bodyToMono(GoogleUserInfoResponse.class) + .block(); + } + +} diff --git a/moodoodle-infrastructure/src/main/java/zzangdol/oauth/client/KakaoLoginClient.java b/moodoodle-infrastructure/src/main/java/zzangdol/oauth/client/kakao/KakaoLoginClient.java similarity index 89% rename from moodoodle-infrastructure/src/main/java/zzangdol/oauth/client/KakaoLoginClient.java rename to moodoodle-infrastructure/src/main/java/zzangdol/oauth/client/kakao/KakaoLoginClient.java index d626d0b..2a868c2 100644 --- a/moodoodle-infrastructure/src/main/java/zzangdol/oauth/client/KakaoLoginClient.java +++ b/moodoodle-infrastructure/src/main/java/zzangdol/oauth/client/kakao/KakaoLoginClient.java @@ -1,7 +1,8 @@ -package zzangdol.oauth.client; +package zzangdol.oauth.client.kakao; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; +import zzangdol.oauth.client.SocialLoginClient; import zzangdol.oauth.dto.KakaoTokenResponse; import zzangdol.oauth.dto.KakaoUserInfoResponse; diff --git a/moodoodle-infrastructure/src/main/java/zzangdol/oauth/client/KakaoLoginTokenClient.java b/moodoodle-infrastructure/src/main/java/zzangdol/oauth/client/kakao/KakaoLoginTokenClient.java similarity index 98% rename from moodoodle-infrastructure/src/main/java/zzangdol/oauth/client/KakaoLoginTokenClient.java rename to moodoodle-infrastructure/src/main/java/zzangdol/oauth/client/kakao/KakaoLoginTokenClient.java index dda7add..1d72423 100644 --- a/moodoodle-infrastructure/src/main/java/zzangdol/oauth/client/KakaoLoginTokenClient.java +++ b/moodoodle-infrastructure/src/main/java/zzangdol/oauth/client/kakao/KakaoLoginTokenClient.java @@ -1,4 +1,4 @@ -package zzangdol.oauth.client; +package zzangdol.oauth.client.kakao; import jakarta.annotation.PostConstruct; import lombok.extern.slf4j.Slf4j; diff --git a/moodoodle-infrastructure/src/main/java/zzangdol/oauth/client/KakaoLoginUserClient.java b/moodoodle-infrastructure/src/main/java/zzangdol/oauth/client/kakao/KakaoLoginUserClient.java similarity index 95% rename from moodoodle-infrastructure/src/main/java/zzangdol/oauth/client/KakaoLoginUserClient.java rename to moodoodle-infrastructure/src/main/java/zzangdol/oauth/client/kakao/KakaoLoginUserClient.java index a566eaf..5b27527 100644 --- a/moodoodle-infrastructure/src/main/java/zzangdol/oauth/client/KakaoLoginUserClient.java +++ b/moodoodle-infrastructure/src/main/java/zzangdol/oauth/client/kakao/KakaoLoginUserClient.java @@ -1,4 +1,4 @@ -package zzangdol.oauth.client; +package zzangdol.oauth.client.kakao; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; diff --git a/moodoodle-infrastructure/src/main/java/zzangdol/oauth/dto/GoogleTokenResponse.java b/moodoodle-infrastructure/src/main/java/zzangdol/oauth/dto/GoogleTokenResponse.java new file mode 100644 index 0000000..3cfd618 --- /dev/null +++ b/moodoodle-infrastructure/src/main/java/zzangdol/oauth/dto/GoogleTokenResponse.java @@ -0,0 +1,22 @@ +package zzangdol.oauth.dto; + +import com.fasterxml.jackson.databind.PropertyNamingStrategies; +import com.fasterxml.jackson.databind.annotation.JsonNaming; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) +public class GoogleTokenResponse { + + private String accessToken; + private int expiresIn; + private String refreshToken; + private String scope; + private String tokenType; + private String idToken; + +} \ No newline at end of file diff --git a/moodoodle-infrastructure/src/main/java/zzangdol/oauth/dto/GoogleUserInfoResponse.java b/moodoodle-infrastructure/src/main/java/zzangdol/oauth/dto/GoogleUserInfoResponse.java new file mode 100644 index 0000000..8bdf307 --- /dev/null +++ b/moodoodle-infrastructure/src/main/java/zzangdol/oauth/dto/GoogleUserInfoResponse.java @@ -0,0 +1,20 @@ +package zzangdol.oauth.dto; + +import lombok.AllArgsConstructor; + +@AllArgsConstructor +public class GoogleUserInfoResponse implements SocialUserInfoResponse { + + private String email; + private String name; + + @Override + public String getEmail() { + return this.email; + } + + @Override + public String getNickname() { + return this.name; + } +}