Skip to content

Commit

Permalink
Merge pull request #266 from MOONSHOT-Team/feature/#264
Browse files Browse the repository at this point in the history
[Refactor] #264 - 유저 소셜 로그인 전략 패턴 적용
  • Loading branch information
its-sky authored Apr 12, 2024
2 parents 7e01150 + 13177d2 commit 1b0e577
Show file tree
Hide file tree
Showing 9 changed files with 263 additions and 118 deletions.
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
package org.moonshot.user.controller;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.Valid;
import java.io.IOException;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.moonshot.jwt.TokenResponse;
Expand All @@ -16,7 +13,6 @@
import org.moonshot.user.dto.response.UserInfoResponse;
import org.moonshot.user.model.LoginUser;
import org.moonshot.user.service.UserService;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
Expand All @@ -38,7 +34,7 @@ public class UserController implements UserApi {
@PostMapping("/login")
@Logging(item = "User", action = "Post")
public ResponseEntity<MoonshotResponse<SocialLoginResponse>> login(@RequestHeader("Authorization") final String authorization,
@RequestBody final SocialLoginRequest socialLoginRequest) throws IOException {
@RequestBody final SocialLoginRequest socialLoginRequest) {
return ResponseEntity.ok(MoonshotResponse.success(SuccessType.POST_LOGIN_SUCCESS, userService.login(SocialLoginRequest.of(socialLoginRequest.socialPlatform(), authorization))));
}

Expand Down
117 changes: 7 additions & 110 deletions moonshot-api/src/main/java/org/moonshot/user/service/UserService.java
Original file line number Diff line number Diff line change
@@ -1,40 +1,27 @@
package org.moonshot.user.service;

import static org.moonshot.response.ErrorType.NOT_FOUND_USER;
import static org.moonshot.response.ErrorType.NOT_SUPPORTED_LOGIN_PLATFORM;
import static org.moonshot.user.service.validator.UserValidator.hasChange;
import static org.moonshot.user.service.validator.UserValidator.isNewUser;
import static org.moonshot.user.service.validator.UserValidator.validateUserAuthorization;
import static org.moonshot.util.MDCUtil.USER_REQUEST_ORIGIN;
import static org.moonshot.util.MDCUtil.get;

import java.time.LocalDateTime;
import java.util.List;
import java.util.Optional;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.moonshot.discord.SignUpEvent;
import org.moonshot.exception.BadRequestException;
import org.moonshot.exception.NotFoundException;
import org.moonshot.jwt.JwtTokenProvider;
import org.moonshot.jwt.TokenResponse;
import org.moonshot.objective.service.ObjectiveService;
import org.moonshot.openfeign.dto.response.google.GoogleInfoResponse;
import org.moonshot.openfeign.dto.response.google.GoogleTokenResponse;
import org.moonshot.openfeign.dto.response.kakao.KakaoTokenResponse;
import org.moonshot.openfeign.dto.response.kakao.KakaoUserResponse;
import org.moonshot.openfeign.google.GoogleApiClient;
import org.moonshot.openfeign.google.GoogleAuthApiClient;
import org.moonshot.openfeign.kakao.KakaoApiClient;
import org.moonshot.openfeign.kakao.KakaoAuthApiClient;
import org.moonshot.user.dto.request.SocialLoginRequest;
import org.moonshot.user.dto.request.UserInfoRequest;
import org.moonshot.user.dto.response.SocialLoginResponse;
import org.moonshot.user.dto.response.UserInfoResponse;
import org.moonshot.user.model.User;
import org.moonshot.user.repository.UserRepository;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationEventPublisher;
import org.moonshot.user.service.social.SocialLoginContext;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

@Slf4j
Expand All @@ -43,95 +30,16 @@
@RequiredArgsConstructor
public class UserService {

@Value("${google.client-id}")
private String googleClientId;

@Value("${google.client-secret}")
private String googleClientSecret;

@Value("${google.redirect-url}")
private String googleRedirectUrl;

@Value("${kakao.client-id}")
private String kakaoClientId;

@Value("${kakao.redirect-uri}")
private String kakaoRedirectUri;

private final UserRepository userRepository;
private final ObjectiveService objectiveService;
private final ApplicationEventPublisher eventPublisher;

private final GoogleAuthApiClient googleAuthApiClient;
private final GoogleApiClient googleApiClient;
private final KakaoAuthApiClient kakaoAuthApiClient;
private final KakaoApiClient kakaoApiClient;
private final JwtTokenProvider jwtTokenProvider;
private final SocialLoginContext socialLoginContext;

public SocialLoginResponse login(final SocialLoginRequest request) {
return switch (request.socialPlatform().getValue()) {
case "google" -> googleLogin(request);
case "kakao" -> kakaoLogin(request);
default -> null;
};
}

public SocialLoginResponse googleLogin(final SocialLoginRequest request) {
GoogleTokenResponse tokenResponse = googleAuthApiClient.googleAuth(
request.code(),
googleClientId,
googleClientSecret,
googleRedirectUrl,
"authorization_code"
);
GoogleInfoResponse userResponse = googleApiClient.googleInfo("Bearer " + tokenResponse.accessToken());
Optional<User> findUser = userRepository.findUserBySocialId(userResponse.sub());
User user;
if (isNewUser(findUser)) {
User newUser = userRepository.save(User.builderWithSignIn()
.socialId(userResponse.sub())
.socialPlatform(request.socialPlatform())
.name(userResponse.name())
.imageUrl(userResponse.picture())
.email(userResponse.email())
.build());
user = newUser;
publishSignUpEvent(newUser);
} else {
user = findUser.get();
user.resetDeleteAt();
}
TokenResponse token = new TokenResponse(jwtTokenProvider.generateAccessToken(user.getId()), jwtTokenProvider.generateRefreshToken(user.getId()));
return SocialLoginResponse.of(user.getId(), user.getName(), token);
}

public SocialLoginResponse kakaoLogin(final SocialLoginRequest request) {
KakaoTokenResponse tokenResponse = kakaoAuthApiClient.getOAuth2AccessToken(
"authorization_code",
kakaoClientId,
(String)get(USER_REQUEST_ORIGIN) + kakaoRedirectUri,
request.code()
);
KakaoUserResponse userResponse = kakaoApiClient.getUserInformation(
"Bearer " + tokenResponse.accessToken());
Optional<User> findUser = userRepository.findUserBySocialId(userResponse.id());
User user;
if (isNewUser(findUser)) {
User newUser = userRepository.save(User.builderWithSignIn()
.socialId(userResponse.id())
.socialPlatform(request.socialPlatform())
.name(userResponse.kakaoAccount().profile().nickname())
.imageUrl(userResponse.kakaoAccount().profile().profileImageUrl())
.email(null)
.build());
user = newUser;
publishSignUpEvent(newUser);
} else {
user = findUser.get();
user.resetDeleteAt();
if (socialLoginContext.support(request.socialPlatform())) {
return socialLoginContext.doLogin(request);
}
TokenResponse token = new TokenResponse(jwtTokenProvider.generateAccessToken(user.getId()), jwtTokenProvider.generateRefreshToken(user.getId()));
return SocialLoginResponse.of(user.getId(), user.getName(), token);
throw new BadRequestException(NOT_SUPPORTED_LOGIN_PLATFORM);
}

public TokenResponse reissue(final String refreshToken) {
Expand Down Expand Up @@ -181,17 +89,6 @@ public void updateUserProfileImage(final Long userId, final String imageUrl) {
user.modifyProfileImage(imageUrl);
}

@Transactional(propagation = Propagation.REQUIRES_NEW)
public void publishSignUpEvent(final User user) {
eventPublisher.publishEvent(SignUpEvent.of(
user.getName(),
user.getEmail() == null ? "" : user.getEmail(),
user.getSocialPlatform().toString(),
LocalDateTime.now(),
user.getImageUrl()
));
}

public void softDeleteUser(LocalDateTime currentDate) {
List<User> expiredUserList = userRepository.findIdByDeletedAtBefore(currentDate);
if(!expiredUserList.isEmpty()) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
package org.moonshot.user.service.social;

import static org.moonshot.user.service.validator.UserValidator.isNewUser;

import java.time.LocalDateTime;
import java.util.Optional;
import lombok.RequiredArgsConstructor;
import org.moonshot.discord.SignUpEvent;
import org.moonshot.jwt.JwtTokenProvider;
import org.moonshot.jwt.TokenResponse;
import org.moonshot.openfeign.dto.response.google.GoogleInfoResponse;
import org.moonshot.openfeign.dto.response.google.GoogleTokenResponse;
import org.moonshot.openfeign.google.GoogleApiClient;
import org.moonshot.openfeign.google.GoogleAuthApiClient;
import org.moonshot.user.dto.request.SocialLoginRequest;
import org.moonshot.user.dto.response.SocialLoginResponse;
import org.moonshot.user.model.User;
import org.moonshot.user.repository.UserRepository;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

@Service
@RequiredArgsConstructor
public class GoogleLoginStrategy implements SocialLoginStrategy {

@Value("${google.client-id}")
private String googleClientId;

@Value("${google.client-secret}")
private String googleClientSecret;

@Value("${google.redirect-url}")
private String googleRedirectUrl;

private final GoogleAuthApiClient googleAuthApiClient;
private final GoogleApiClient googleApiClient;

private final ApplicationEventPublisher eventPublisher;
private final JwtTokenProvider jwtTokenProvider;
private final UserRepository userRepository;

@Override
@Transactional
public SocialLoginResponse login(SocialLoginRequest request) {
GoogleTokenResponse tokenResponse = googleAuthApiClient.googleAuth(
request.code(),
googleClientId,
googleClientSecret,
googleRedirectUrl,
"authorization_code"
);
GoogleInfoResponse userResponse = googleApiClient.googleInfo("Bearer " + tokenResponse.accessToken());
Optional<User> findUser = userRepository.findUserBySocialId(userResponse.sub());
User user;
if (isNewUser(findUser)) {
User newUser = userRepository.save(User.builderWithSignIn()
.socialId(userResponse.sub())
.socialPlatform(request.socialPlatform())
.name(userResponse.name())
.imageUrl(userResponse.picture())
.email(userResponse.email())
.build());
user = newUser;
publishSignUpEvent(newUser);
} else {
user = findUser.get();
user.resetDeleteAt();
}
TokenResponse token = new TokenResponse(jwtTokenProvider.generateAccessToken(user.getId()), jwtTokenProvider.generateRefreshToken(user.getId()));
return SocialLoginResponse.of(user.getId(), user.getName(), token);
}

@Override
public boolean support(String provider) {
return provider.equals("GOOGLE");
}

@Override
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void publishSignUpEvent(User user) {
eventPublisher.publishEvent(SignUpEvent.of(
user.getName(),
user.getEmail() == null ? "" : user.getEmail(),
user.getSocialPlatform().toString(),
LocalDateTime.now(),
user.getImageUrl()
));
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
package org.moonshot.user.service.social;

import static org.moonshot.user.service.validator.UserValidator.isNewUser;
import static org.moonshot.util.MDCUtil.USER_REQUEST_ORIGIN;
import static org.moonshot.util.MDCUtil.get;

import java.time.LocalDateTime;
import java.util.Optional;
import lombok.RequiredArgsConstructor;
import org.moonshot.discord.SignUpEvent;
import org.moonshot.jwt.JwtTokenProvider;
import org.moonshot.jwt.TokenResponse;
import org.moonshot.openfeign.dto.response.kakao.KakaoTokenResponse;
import org.moonshot.openfeign.dto.response.kakao.KakaoUserResponse;
import org.moonshot.openfeign.kakao.KakaoApiClient;
import org.moonshot.openfeign.kakao.KakaoAuthApiClient;
import org.moonshot.user.dto.request.SocialLoginRequest;
import org.moonshot.user.dto.response.SocialLoginResponse;
import org.moonshot.user.model.User;
import org.moonshot.user.repository.UserRepository;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

@Service
@RequiredArgsConstructor
public class KakaoLoginStrategy implements SocialLoginStrategy {

@Value("${kakao.client-id}")
private String kakaoClientId;

@Value("${kakao.redirect-uri}")
private String kakaoRedirectUri;

private final KakaoAuthApiClient kakaoAuthApiClient;
private final KakaoApiClient kakaoApiClient;

private final ApplicationEventPublisher eventPublisher;
private final JwtTokenProvider jwtTokenProvider;
private final UserRepository userRepository;

@Override
@Transactional
public SocialLoginResponse login(SocialLoginRequest request) {
KakaoTokenResponse tokenResponse = kakaoAuthApiClient.getOAuth2AccessToken(
"authorization_code",
kakaoClientId,
(String)get(USER_REQUEST_ORIGIN) + kakaoRedirectUri,
request.code()
);
KakaoUserResponse userResponse = kakaoApiClient.getUserInformation(
"Bearer " + tokenResponse.accessToken());
Optional<User> findUser = userRepository.findUserBySocialId(userResponse.id());
User user;
if (isNewUser(findUser)) {
User newUser = userRepository.save(User.builderWithSignIn()
.socialId(userResponse.id())
.socialPlatform(request.socialPlatform())
.name(userResponse.kakaoAccount().profile().nickname())
.imageUrl(userResponse.kakaoAccount().profile().profileImageUrl())
.email(null)
.build());
user = newUser;
publishSignUpEvent(newUser);
} else {
user = findUser.get();
user.resetDeleteAt();
}
TokenResponse token = new TokenResponse(jwtTokenProvider.generateAccessToken(user.getId()), jwtTokenProvider.generateRefreshToken(user.getId()));
return SocialLoginResponse.of(user.getId(), user.getName(), token);
}

@Override
public boolean support(String provider) {
return provider.equals("KAKAO");
}

@Override
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void publishSignUpEvent(User user) {
eventPublisher.publishEvent(SignUpEvent.of(
user.getName(),
user.getEmail() == null ? "" : user.getEmail(),
user.getSocialPlatform().toString(),
LocalDateTime.now(),
user.getImageUrl()
));
}

}
Loading

0 comments on commit 1b0e577

Please sign in to comment.