From 1c8a56226a464b22103fcd8d73c6416f271e993a Mon Sep 17 00:00:00 2001 From: You Jung <80906691+JangYouJung@users.noreply.github.com> Date: Tue, 6 Aug 2024 23:39:38 +0900 Subject: [PATCH 1/3] =?UTF-8?q?Refactor:=20OAuth=20=EB=A1=9C=EA=B7=B8?= =?UTF-8?q?=EC=9D=B8=20=EB=B6=88=ED=95=84=EC=9A=94=ED=95=9C=20=EC=BD=94?= =?UTF-8?q?=EB=93=9C=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../auth/oauth/kakao/KaKaoTokenDto.java | 10 -- .../server/auth/oauth/kakao/KakaoService.java | 137 ------------------ 2 files changed, 147 deletions(-) delete mode 100644 src/main/java/meltingpot/server/auth/oauth/kakao/KaKaoTokenDto.java delete mode 100644 src/main/java/meltingpot/server/auth/oauth/kakao/KakaoService.java diff --git a/src/main/java/meltingpot/server/auth/oauth/kakao/KaKaoTokenDto.java b/src/main/java/meltingpot/server/auth/oauth/kakao/KaKaoTokenDto.java deleted file mode 100644 index 1538024..0000000 --- a/src/main/java/meltingpot/server/auth/oauth/kakao/KaKaoTokenDto.java +++ /dev/null @@ -1,10 +0,0 @@ -package meltingpot.server.auth.oauth.kakao; - -import lombok.Builder; - -@Builder -public record KaKaoTokenDto ( - String accessToken, - String refreshToken -){ -} diff --git a/src/main/java/meltingpot/server/auth/oauth/kakao/KakaoService.java b/src/main/java/meltingpot/server/auth/oauth/kakao/KakaoService.java deleted file mode 100644 index 970dfb8..0000000 --- a/src/main/java/meltingpot/server/auth/oauth/kakao/KakaoService.java +++ /dev/null @@ -1,137 +0,0 @@ -package meltingpot.server.auth.oauth.kakao; - -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import org.json.simple.JSONObject; -import org.json.simple.parser.JSONParser; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.http.HttpEntity; -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpMethod; -import org.springframework.http.ResponseEntity; -import org.springframework.stereotype.Service; -import org.springframework.util.LinkedMultiValueMap; -import org.springframework.util.MultiValueMap; -import org.springframework.web.client.RestTemplate; -import java.util.Base64; - -@Service -public class KakaoService { - - @Value("${spring.security.oauth2.client.registration.kakao.client-id}") - private String KAKAO_CLIENT_ID; - - @Value("${spring.security.oauth2.client.registration.kakao.client-secret}") - private String KAKAO_CLIENT_SECRET; - - @Value("${spring.security.oauth2.client.registration.kakao.redirect-uri}") - private String redirect_uri; - - private final static String KAKAO_AUTH_URI = "https://kauth.kakao.com"; - private final static String KAKAO_API_URI = "https://kapi.kakao.com"; - private final ObjectMapper objectMapper = new ObjectMapper(); - - public String getKakaoLogin(String redirect_uri) { // 프론트 구현부 - return KAKAO_AUTH_URI + "/oauth/authorize" - + "?client_id=" + KAKAO_CLIENT_ID - + "&redirect_uri=" + redirect_uri - + "&response_type=token"; - } - - public KaKaoTokenDto getKakaoToken(String code) throws Exception { - if (code == null) throw new Exception("Failed get authorization token"); - - String accessToken = ""; - String refreshToken = ""; - - try { - HttpHeaders headers = new HttpHeaders(); - headers.add("Content-type", "application/x-www-form-urlencoded"); - - MultiValueMap params = new LinkedMultiValueMap<>(); - params.add("grant_type" , "authorization_code"); - params.add("client_id" , KAKAO_CLIENT_ID); - params.add("client_secret", KAKAO_CLIENT_SECRET); - params.add("token" , code); - params.add("redirect_uri" , redirect_uri); - - RestTemplate restTemplate = new RestTemplate(); - HttpEntity> httpEntity = new HttpEntity<>(params, headers); - - ResponseEntity response = restTemplate.exchange( - KAKAO_AUTH_URI + "/oauth/token", - HttpMethod.POST, - httpEntity, - String.class - ); - - JSONParser jsonParser = new JSONParser(); - JSONObject jsonObj = (JSONObject) jsonParser.parse(response.getBody()); - - accessToken = (String) jsonObj.get("access_token"); - refreshToken = (String) jsonObj.get("refresh_token"); - } catch (Exception e) { - throw new Exception("KAKAO API call failed"); - } - - return KaKaoTokenDto.builder() - .accessToken(accessToken) - .refreshToken(refreshToken) - .build(); - } - - public KakaoDto getUserInfoWithToken(String accessToken) throws Exception { - //HttpHeader 생성 - HttpHeaders headers = new HttpHeaders(); - headers.add("Authorization", "Bearer " + accessToken); - headers.add("Content-type", "application/x-www-form-urlencoded;charset=utf-8"); - - //HttpHeader 담기 - RestTemplate rt = new RestTemplate(); - HttpEntity> httpEntity = new HttpEntity<>(headers); - ResponseEntity response = rt.exchange( - KAKAO_API_URI + "/v2/user/me", - HttpMethod.POST, - httpEntity, - String.class - ); - - //Response 데이터 파싱 - JSONParser jsonParser = new JSONParser(); - JSONObject jsonObj = (JSONObject) jsonParser.parse(response.getBody()); - JSONObject account = (JSONObject) jsonObj.get("kakao_account"); - JSONObject profile = (JSONObject) account.get("profile"); - - long id = (long) jsonObj.get("id"); - String email = String.valueOf(account.get("email")); - String nickname = String.valueOf(profile.get("nickname")); - - return KakaoDto.builder() - .email(email) - .nickname(nickname).build(); - } - - public KakaoDto getUserInfoFromIdToken(String idToken) throws Exception { - // 온점 분리 - String[] parts = idToken.split("\\."); - if (parts.length != 3) { - throw new IllegalArgumentException("Invalid IdToken"); - } - - // Payload 디코딩 - String payload = parts[1]; - String decodedPayload = new String(Base64.getDecoder().decode(payload)); - - // JSON 파싱 - JsonNode jsonNode = objectMapper.readTree(decodedPayload); - - // email과 nickname 추출 - String email = jsonNode.path("email").asText(null); - String nickname = jsonNode.path("nickname").asText(null); - - // 정보 담아 보내기 - return KakaoDto.builder() - .email(email) - .nickname(nickname).build(); - } -} \ No newline at end of file From dc575b3fdaf1be54e0030708b6bef7a850a8174f Mon Sep 17 00:00:00 2001 From: You Jung <80906691+JangYouJung@users.noreply.github.com> Date: Tue, 6 Aug 2024 23:40:05 +0900 Subject: [PATCH 2/3] =?UTF-8?q?Refactor:=20OAuth=20=EB=A1=9C=EA=B7=B8?= =?UTF-8?q?=EC=9D=B8=20=EB=B6=88=ED=95=84=EC=9A=94=ED=95=9C=20=EC=BD=94?= =?UTF-8?q?=EB=93=9C=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../meltingpot/server/auth/oauth/kakao/KakaoDto.java | 11 ----------- 1 file changed, 11 deletions(-) delete mode 100644 src/main/java/meltingpot/server/auth/oauth/kakao/KakaoDto.java diff --git a/src/main/java/meltingpot/server/auth/oauth/kakao/KakaoDto.java b/src/main/java/meltingpot/server/auth/oauth/kakao/KakaoDto.java deleted file mode 100644 index eb279b4..0000000 --- a/src/main/java/meltingpot/server/auth/oauth/kakao/KakaoDto.java +++ /dev/null @@ -1,11 +0,0 @@ -package meltingpot.server.auth.oauth.kakao; - -import lombok.Builder; -import lombok.Data; -@Builder -@Data -public class KakaoDto { - private String email; - private String nickname; - -} \ No newline at end of file From 1d3962af5ad2b05994b4993981cb7397b6178045 Mon Sep 17 00:00:00 2001 From: You Jung <80906691+JangYouJung@users.noreply.github.com> Date: Tue, 6 Aug 2024 23:41:35 +0900 Subject: [PATCH 3/3] =?UTF-8?q?feature:=20=EC=95=A0=ED=94=8C=20=EB=A1=9C?= =?UTF-8?q?=EA=B7=B8=EC=9D=B8=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 1 + .../server/auth/oauth/OAuthDto.java | 12 +++ .../server/auth/service/AuthService.java | 2 +- .../server/auth/service/OAuthService.java | 99 +++++++++---------- .../server/domain/entity/Account.java | 3 +- .../domain/repository/AccountRepository.java | 2 + 6 files changed, 67 insertions(+), 52 deletions(-) create mode 100644 src/main/java/meltingpot/server/auth/oauth/OAuthDto.java diff --git a/build.gradle b/build.gradle index facd766..15e5a1e 100644 --- a/build.gradle +++ b/build.gradle @@ -61,6 +61,7 @@ dependencies { //Apple Login implementation 'com.nimbusds:nimbus-jose-jwt:3.10' + implementation 'org.bouncycastle:bcpkix-jdk18on:1.72' //Json implementation 'com.googlecode.json-simple:json-simple:1.1.1' diff --git a/src/main/java/meltingpot/server/auth/oauth/OAuthDto.java b/src/main/java/meltingpot/server/auth/oauth/OAuthDto.java new file mode 100644 index 0000000..654059f --- /dev/null +++ b/src/main/java/meltingpot/server/auth/oauth/OAuthDto.java @@ -0,0 +1,12 @@ +package meltingpot.server.auth.oauth; + +import lombok.Builder; +import lombok.Getter; + +@Builder +@Getter +public class OAuthDto { + private String email; + private String nickname; + +} \ No newline at end of file diff --git a/src/main/java/meltingpot/server/auth/service/AuthService.java b/src/main/java/meltingpot/server/auth/service/AuthService.java index 94f56af..1ceb488 100644 --- a/src/main/java/meltingpot/server/auth/service/AuthService.java +++ b/src/main/java/meltingpot/server/auth/service/AuthService.java @@ -106,7 +106,7 @@ public AccountResponseDto signup(SignupRequestDto signupRequest) { .birth(signupRequest.birth()) .nationality(signupRequest.nationality()) .isQuit(false) - .oAuthType(OAuthType.NONE) + .OAuthType(OAuthType.NONE) .build(); account.setProfileImages(signupRequest.profileImages().stream().map( diff --git a/src/main/java/meltingpot/server/auth/service/OAuthService.java b/src/main/java/meltingpot/server/auth/service/OAuthService.java index ab97ca8..bad420f 100644 --- a/src/main/java/meltingpot/server/auth/service/OAuthService.java +++ b/src/main/java/meltingpot/server/auth/service/OAuthService.java @@ -1,18 +1,18 @@ package meltingpot.server.auth.service; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import meltingpot.server.auth.controller.dto.OAuthSignInRequestDto; import meltingpot.server.auth.controller.dto.OAuthSignupRequestDto; import meltingpot.server.auth.controller.dto.ProfileImageRequestDto; import meltingpot.server.auth.oauth.OAuthUserDetails; -import meltingpot.server.auth.oauth.kakao.KakaoDto; -import meltingpot.server.auth.oauth.kakao.KakaoService; +import meltingpot.server.auth.oauth.OAuthDto; import meltingpot.server.auth.service.dto.OAuthSignInResponseDto; import meltingpot.server.config.TokenProvider; import meltingpot.server.domain.entity.*; import meltingpot.server.domain.entity.enums.Gender; -import meltingpot.server.domain.entity.enums.OAuthType; import meltingpot.server.domain.repository.AccountPushTokenRepository; import meltingpot.server.domain.repository.AccountRepository; import meltingpot.server.domain.repository.RefreshTokenRepository; @@ -27,10 +27,7 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import java.util.HashSet; -import java.util.NoSuchElementException; -import java.util.Optional; -import java.util.Set; +import java.util.*; @Slf4j @RequiredArgsConstructor @@ -38,11 +35,10 @@ @EnableWebSecurity public class OAuthService { private final AccountRepository accountRepository; - private final KakaoService kakaoService; private final TokenProvider tokenProvider; private final RefreshTokenRepository refreshTokenRepository; private final AccountPushTokenRepository accountPushTokenRepository; - + private final ObjectMapper objectMapper = new ObjectMapper(); // SNS 회원 가입 @Transactional @@ -90,7 +86,7 @@ public OAuthSignInResponseDto oauthSignup(OAuthSignupRequestDto signupRequest) { .birth(signupRequest.birth()) .nationality(signupRequest.nationality()) .isQuit(false) - .oAuthType(signupRequest.OauthType()) + .OAuthType(signupRequest.OauthType()) .build(); account.setProfileImages(signupRequest.profileImages().stream().map( @@ -126,49 +122,27 @@ public OAuthSignInResponseDto oauthSignup(OAuthSignupRequestDto signupRequest) { @Transactional public OAuthSignInResponseDto SNSLogin(OAuthSignInRequestDto request) throws Exception { - if(request.type() == OAuthType.KAKAO) { - - /* * * * RestAPI 버전 * * * / - // 카카오 토큰 가져오기 - KaKaoTokenDto tokenDto = kakaoService.getKakaoToken(request.token()); - - // 카카오 유저 정보 가져오기 - KakaoDto kakaoDto = kakaoService.getUserInfoWithToken(request.token()); - * * * * * * * * * * * * */ - - // SDK 버전: 아이디토큰으로 유저 정보 파싱해오기 - KakaoDto kakaoDto = kakaoService.getUserInfoFromIdToken(request.token()); - - // 이미 가입한 회원인지 확인 - Optional account = accountRepository.findByUsernameAndIsQuitIsFalse(kakaoDto.getEmail()); - if (account.isEmpty()) { + OAuthDto oAuthDto = getUserInfoFromIdToken(request.token()); - // 회원 가입이 필요한 경우 - return OAuthSignInResponseDto.builder() - .register_required(true) - .nickName(kakaoDto.getNickname()) - .email(kakaoDto.getEmail()) - .tokenDto(null) - .build(); + // 이미 가입한 회원인지 확인 + Optional account = accountRepository.findByUsernameAndIsQuitFalseAndOAuthType(oAuthDto.getEmail(), request.type()); + if (account.isEmpty()) { - } else { + // 회원 가입이 필요한 경우 + return OAuthSignInResponseDto.builder() + .register_required(true) + .nickName(oAuthDto.getNickname()) + .email(oAuthDto.getEmail()) + .tokenDto(null) + .build(); - return OAuthSignInResponseDto.builder(). - register_required(false) - .nickName(kakaoDto.getNickname()) - .email(kakaoDto.getEmail()) - .tokenDto(setSecurityContext(account.get(), request.push_token())) - .build(); - } - } -// else if(request.type() == OAuthType.APPLE) { -// -// } -// else if(request.type() == OAuthType.GOOGLE) { -// -// } - else { - throw new NoSuchElementException(); + } else { + return OAuthSignInResponseDto.builder(). + register_required(false) + .nickName(oAuthDto.getNickname()) + .email(oAuthDto.getEmail()) + .tokenDto(setSecurityContext(account.get(), request.push_token())) + .build(); } } @@ -208,4 +182,29 @@ public TokenDto setSecurityContext(Account account, String pushToken ){ } + public OAuthDto getUserInfoFromIdToken(String idToken) throws Exception { + + // 온점 분리 + String[] parts = idToken.split("\\."); + if (parts.length != 3) { + throw new java.lang.IllegalArgumentException("Invalid IdToken"); + } + + // Payload 디코딩 + String payload = parts[1]; + String decodedPayload = new String(Base64.getDecoder().decode(payload)); + + // JSON 파싱 + JsonNode jsonNode = objectMapper.readTree(decodedPayload); + + // email과 nickname 추출 + String email = jsonNode.path("email").asText(null); + String nickname = jsonNode.path("nickname").asText(null); + + return OAuthDto.builder() + .email(email) + .nickname(nickname).build(); + + } + } diff --git a/src/main/java/meltingpot/server/domain/entity/Account.java b/src/main/java/meltingpot/server/domain/entity/Account.java index 6521063..d33de76 100644 --- a/src/main/java/meltingpot/server/domain/entity/Account.java +++ b/src/main/java/meltingpot/server/domain/entity/Account.java @@ -61,7 +61,8 @@ public class Account extends BaseEntity { private Boolean isQuit; // 탈퇴 여부 - private OAuthType oAuthType; + @Enumerated(EnumType.STRING) + private OAuthType OAuthType; @OneToMany(mappedBy = "account", cascade = CascadeType.ALL, fetch = FetchType.EAGER) @Builder.Default diff --git a/src/main/java/meltingpot/server/domain/repository/AccountRepository.java b/src/main/java/meltingpot/server/domain/repository/AccountRepository.java index a3230c6..f8974de 100644 --- a/src/main/java/meltingpot/server/domain/repository/AccountRepository.java +++ b/src/main/java/meltingpot/server/domain/repository/AccountRepository.java @@ -1,6 +1,7 @@ package meltingpot.server.domain.repository; import meltingpot.server.domain.entity.Account; +import meltingpot.server.domain.entity.enums.OAuthType; import org.springframework.data.jpa.repository.EntityGraph; import org.springframework.data.jpa.repository.JpaRepository; @@ -8,6 +9,7 @@ public interface AccountRepository extends JpaRepository { Optional findByUsernameAndIsQuitIsFalse(String username); + Optional findByUsernameAndIsQuitFalseAndOAuthType(String username, OAuthType oAuthType); Optional findByUsername(String name); boolean existsByUsername(String username);