From 6d50e1d0f52a93f0ed4d4f86604be2bfc6714538 Mon Sep 17 00:00:00 2001 From: hee9841 Date: Sat, 3 Aug 2024 14:10:14 +0900 Subject: [PATCH 01/19] =?UTF-8?q?Chore:=20apple=20public-key-url=20?= =?UTF-8?q?=EC=84=A4=EC=A0=95=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/application.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index c12d4939..90a20eaa 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -25,6 +25,10 @@ app: expiration: ${ACCESS_TOKEN_EXPIRATION} secret-key: ${ACCESS_TOKEN_SECRET_KEY} +oauth: + apple: + public-key-url: ${APPLE_AUTH_URL} + --- spring.config.activate.on-profile: local From 6515da300f05238f513dd53adc0c17f084864862 Mon Sep 17 00:00:00 2001 From: hee9841 Date: Sat, 3 Aug 2024 13:32:42 +0900 Subject: [PATCH 02/19] =?UTF-8?q?Feat:=20=EC=95=A0=ED=94=8C=20=ED=8D=BC?= =?UTF-8?q?=EB=B8=94=EB=A6=AC=ED=82=A4=20=EC=83=9D=EC=84=B1=20=EA=B8=B0?= =?UTF-8?q?=EB=8A=A5=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../runus/auth/apple/PublicKeyProvider.java | 46 +++++++++++++++++++ .../runus/client/config/RestClientConfig.java | 34 ++++++++++++++ .../dnd/runus/client/vo/ApplePublicKey.java | 3 ++ .../client/vo/ApplePublicKeyResponse.java | 18 ++++++++ .../dnd/runus/client/web/AppleAuthClient.java | 16 +++++++ .../client/web/AppleAuthClientComponent.java | 13 ++++++ 6 files changed, 130 insertions(+) create mode 100644 src/main/java/com/dnd/runus/auth/apple/PublicKeyProvider.java create mode 100644 src/main/java/com/dnd/runus/client/config/RestClientConfig.java create mode 100644 src/main/java/com/dnd/runus/client/vo/ApplePublicKey.java create mode 100644 src/main/java/com/dnd/runus/client/vo/ApplePublicKeyResponse.java create mode 100644 src/main/java/com/dnd/runus/client/web/AppleAuthClient.java create mode 100644 src/main/java/com/dnd/runus/client/web/AppleAuthClientComponent.java diff --git a/src/main/java/com/dnd/runus/auth/apple/PublicKeyProvider.java b/src/main/java/com/dnd/runus/auth/apple/PublicKeyProvider.java new file mode 100644 index 00000000..f2f48fb6 --- /dev/null +++ b/src/main/java/com/dnd/runus/auth/apple/PublicKeyProvider.java @@ -0,0 +1,46 @@ +package com.dnd.runus.auth.apple; + +import com.dnd.runus.client.vo.ApplePublicKey; +import com.dnd.runus.client.vo.ApplePublicKeyResponse; +import com.dnd.runus.global.exception.BusinessException; +import com.dnd.runus.global.exception.type.ErrorType; +import org.springframework.stereotype.Component; + +import java.math.BigInteger; +import java.security.KeyFactory; +import java.security.NoSuchAlgorithmException; +import java.security.PublicKey; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.RSAPublicKeySpec; +import java.util.Base64; +import java.util.Map; + +@Component +public class PublicKeyProvider { + + public PublicKey generatePublicKey( + final Map tokenHeaders, final ApplePublicKeyResponse publicKeys) { + + ApplePublicKey publicKey = publicKeys.getMatchedKeyBy(tokenHeaders.get("kid"), tokenHeaders.get("alg")); + + return getPublicKey(publicKey); + } + + private PublicKey getPublicKey(final ApplePublicKey key) { + + try { + final byte[] nByte = Base64.getUrlDecoder().decode(key.n()); + final byte[] eByte = Base64.getUrlDecoder().decode(key.e()); + + BigInteger n = new BigInteger(1, nByte); + BigInteger e = new BigInteger(1, eByte); + + RSAPublicKeySpec publicKeySpec = new RSAPublicKeySpec(n, e); + KeyFactory keyFactory = KeyFactory.getInstance(key.kty()); + + return keyFactory.generatePublic(publicKeySpec); + } catch (NoSuchAlgorithmException | InvalidKeySpecException e) { + throw new BusinessException(ErrorType.UNSUPPORTED_JWT_TOKEN, e.getMessage()); + } + } +} diff --git a/src/main/java/com/dnd/runus/client/config/RestClientConfig.java b/src/main/java/com/dnd/runus/client/config/RestClientConfig.java new file mode 100644 index 00000000..762cab67 --- /dev/null +++ b/src/main/java/com/dnd/runus/client/config/RestClientConfig.java @@ -0,0 +1,34 @@ +package com.dnd.runus.client.config; + +import com.dnd.runus.client.web.AppleAuthClientComponent; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.web.client.ClientHttpRequestFactories; +import org.springframework.boot.web.client.ClientHttpRequestFactorySettings; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.client.ClientHttpRequestFactory; +import org.springframework.web.client.RestClient; +import org.springframework.web.client.support.RestClientAdapter; +import org.springframework.web.service.invoker.HttpServiceProxyFactory; + +import java.time.Duration; + +@Configuration +public class RestClientConfig { + + @Bean + public AppleAuthClientComponent appleAuthClientService(@Value("${oauth.apple.public-key-url}") String url) { + ClientHttpRequestFactorySettings settings = ClientHttpRequestFactorySettings.DEFAULTS + .withReadTimeout(Duration.ofSeconds(5)) + .withConnectTimeout(Duration.ofSeconds(10)); + ClientHttpRequestFactory requestFactory = ClientHttpRequestFactories.get(settings); + + RestClient restClient = + RestClient.builder().baseUrl(url).requestFactory(requestFactory).build(); + RestClientAdapter adapter = RestClientAdapter.create(restClient); + HttpServiceProxyFactory factory = + HttpServiceProxyFactory.builderFor(adapter).build(); + + return factory.createClient(AppleAuthClientComponent.class); + } +} diff --git a/src/main/java/com/dnd/runus/client/vo/ApplePublicKey.java b/src/main/java/com/dnd/runus/client/vo/ApplePublicKey.java new file mode 100644 index 00000000..a682e103 --- /dev/null +++ b/src/main/java/com/dnd/runus/client/vo/ApplePublicKey.java @@ -0,0 +1,3 @@ +package com.dnd.runus.client.vo; + +public record ApplePublicKey(String kty, String kid, String use, String alg, String n, String e) {} diff --git a/src/main/java/com/dnd/runus/client/vo/ApplePublicKeyResponse.java b/src/main/java/com/dnd/runus/client/vo/ApplePublicKeyResponse.java new file mode 100644 index 00000000..194c87fb --- /dev/null +++ b/src/main/java/com/dnd/runus/client/vo/ApplePublicKeyResponse.java @@ -0,0 +1,18 @@ +package com.dnd.runus.client.vo; + + +import com.dnd.runus.global.exception.BusinessException; +import com.dnd.runus.global.exception.type.ErrorType; +import java.util.List; + +public record ApplePublicKeyResponse(List keys) { + + public ApplePublicKey getMatchedKeyBy(String kid, String alg) { + //kid, alg 일치한 퍼블릭 키 + return keys.stream() + .filter(key -> key.kid().equals(kid) && key.alg().equals(alg)) + .findAny() + .orElseThrow(() -> new BusinessException(ErrorType.MALFORMED_ACCESS_TOKEN, "유효하지 않은 아이디입니다.")); + } + +} diff --git a/src/main/java/com/dnd/runus/client/web/AppleAuthClient.java b/src/main/java/com/dnd/runus/client/web/AppleAuthClient.java new file mode 100644 index 00000000..872dd1bd --- /dev/null +++ b/src/main/java/com/dnd/runus/client/web/AppleAuthClient.java @@ -0,0 +1,16 @@ +package com.dnd.runus.client.web; + +import com.dnd.runus.client.vo.ApplePublicKeyResponse; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; + +@Component +@RequiredArgsConstructor +public class AppleAuthClient { + + private final AppleAuthClientComponent appleAuthClientComponent; + + public ApplePublicKeyResponse getPublicKeys() { + return appleAuthClientComponent.getPublicKeys(); + } +} diff --git a/src/main/java/com/dnd/runus/client/web/AppleAuthClientComponent.java b/src/main/java/com/dnd/runus/client/web/AppleAuthClientComponent.java new file mode 100644 index 00000000..631fcb70 --- /dev/null +++ b/src/main/java/com/dnd/runus/client/web/AppleAuthClientComponent.java @@ -0,0 +1,13 @@ +package com.dnd.runus.client.web; + +import com.dnd.runus.client.vo.ApplePublicKeyResponse; +import org.springframework.stereotype.Component; +import org.springframework.web.service.annotation.GetExchange; +import org.springframework.web.service.annotation.HttpExchange; + +@Component +@HttpExchange +public interface AppleAuthClientComponent { + @GetExchange("/keys") + ApplePublicKeyResponse getPublicKeys(); +} From 75a473413b0097750cbddbaf50844090cb440348 Mon Sep 17 00:00:00 2001 From: hee9841 Date: Sat, 3 Aug 2024 13:35:16 +0900 Subject: [PATCH 03/19] =?UTF-8?q?Feat:=20apple=20id=20token=20parser=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../runus/auth/apple/AppleAuthProvider.java | 61 +++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 src/main/java/com/dnd/runus/auth/apple/AppleAuthProvider.java diff --git a/src/main/java/com/dnd/runus/auth/apple/AppleAuthProvider.java b/src/main/java/com/dnd/runus/auth/apple/AppleAuthProvider.java new file mode 100644 index 00000000..ae8632f4 --- /dev/null +++ b/src/main/java/com/dnd/runus/auth/apple/AppleAuthProvider.java @@ -0,0 +1,61 @@ +package com.dnd.runus.auth.apple; + +import com.dnd.runus.auth.exception.AuthException; +import com.dnd.runus.client.vo.ApplePublicKeyResponse; +import com.dnd.runus.client.web.AppleAuthClient; +import com.dnd.runus.global.exception.BusinessException; +import com.dnd.runus.global.exception.type.ErrorType; +import com.fasterxml.jackson.databind.ObjectMapper; +import io.jsonwebtoken.Claims; +import io.jsonwebtoken.ExpiredJwtException; +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.MalformedJwtException; +import io.jsonwebtoken.security.SignatureException; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; + +import java.io.IOException; +import java.security.PublicKey; +import java.util.Base64; +import java.util.Map; + +@Component +@RequiredArgsConstructor +public class AppleAuthProvider { + + private final AppleAuthClient appleAuthClient; + private final PublicKeyProvider publicKeyProvider; + + public Claims getClaimsBy(String identityToken) { + // 퍼블릭 키 리스트 + ApplePublicKeyResponse publicKeys = appleAuthClient.getPublicKeys(); + // 토큰 헤더에서 디코딩 -> 퍼블릭 키 리스트 대조회 n,e갑 디코딩 후 퍼블릭 키 생성 + PublicKey publicKey = publicKeyProvider.generatePublicKey(parseHeaders(identityToken), publicKeys); + + return parseClaims(identityToken, publicKey); + } + + private Map parseHeaders(String token) { + String header = token.split("\\.")[0]; + try { + return new ObjectMapper().readValue(Base64.getUrlDecoder().decode(header), Map.class); + } catch (IOException e) { + throw new BusinessException(ErrorType.FAILED_PARSING, e.getMessage()); + } + } + + private Claims parseClaims(String token, PublicKey publicKey) { + try { + return Jwts.parser() + .verifyWith(publicKey) + .build() + .parseSignedClaims(token) + .getPayload(); + } catch (SignatureException | MalformedJwtException e) { + // 토큰 서명 검증 또는 구조 문제 + throw new AuthException(ErrorType.MALFORMED_ACCESS_TOKEN, e.getMessage()); + } catch (ExpiredJwtException e) { + throw new AuthException(ErrorType.INVALID_ACCESS_TOKEN, e.getMessage()); + } + } +} From 4d935a147a614d37355b7416fb3bc2d8ae90470a Mon Sep 17 00:00:00 2001 From: hee9841 Date: Sat, 3 Aug 2024 13:44:58 +0900 Subject: [PATCH 04/19] =?UTF-8?q?Feat:=20=EB=A1=9C=EA=B7=B8=EC=9D=B8=20?= =?UTF-8?q?=EB=B0=8F=20=ED=9A=8C=EC=9B=90=EA=B0=80=EC=9E=85=20=EB=B9=84?= =?UTF-8?q?=EC=A7=80=EB=8B=88=EC=8A=A4=20=EB=A1=9C=EC=A7=81=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../login/dto/request/LoginRequest.java | 20 +++++ .../login/dto/response/TokenResponse.java | 16 ++++ .../domain/login/service/LoginService.java | 84 +++++++++++++++++++ .../domain/member/entity/SocialProfile.java | 5 ++ .../repository/SocialProfileRepository.java | 15 ++++ 5 files changed, 140 insertions(+) create mode 100644 src/main/java/com/dnd/runus/domain/login/dto/request/LoginRequest.java create mode 100644 src/main/java/com/dnd/runus/domain/login/dto/response/TokenResponse.java create mode 100644 src/main/java/com/dnd/runus/domain/login/service/LoginService.java create mode 100644 src/main/java/com/dnd/runus/domain/member/repository/SocialProfileRepository.java diff --git a/src/main/java/com/dnd/runus/domain/login/dto/request/LoginRequest.java b/src/main/java/com/dnd/runus/domain/login/dto/request/LoginRequest.java new file mode 100644 index 00000000..eec456f4 --- /dev/null +++ b/src/main/java/com/dnd/runus/domain/login/dto/request/LoginRequest.java @@ -0,0 +1,20 @@ +package com.dnd.runus.domain.login.dto.request; + +import com.dnd.runus.global.constant.SocialType; +import jakarta.validation.constraints.Email; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; + + +public record LoginRequest( + @NotNull + SocialType socialType, + @NotBlank + String idToken, + @NotBlank + @Email + String email, + @NotBlank + String nickName +){ +} diff --git a/src/main/java/com/dnd/runus/domain/login/dto/response/TokenResponse.java b/src/main/java/com/dnd/runus/domain/login/dto/response/TokenResponse.java new file mode 100644 index 00000000..7f18f882 --- /dev/null +++ b/src/main/java/com/dnd/runus/domain/login/dto/response/TokenResponse.java @@ -0,0 +1,16 @@ +package com.dnd.runus.domain.login.dto.response; + + +import com.dnd.runus.auth.token.dto.AuthTokenDto; + +public record TokenResponse( + String accessToken, +//todo refresh token 구현 되면 +// String refreshToken, + String grantType +) { + + public static TokenResponse from(AuthTokenDto tokenDto) { + return new TokenResponse(tokenDto.accessToken(), tokenDto.type()); + } +} diff --git a/src/main/java/com/dnd/runus/domain/login/service/LoginService.java b/src/main/java/com/dnd/runus/domain/login/service/LoginService.java new file mode 100644 index 00000000..1499421a --- /dev/null +++ b/src/main/java/com/dnd/runus/domain/login/service/LoginService.java @@ -0,0 +1,84 @@ +package com.dnd.runus.domain.login.service; + +import com.dnd.runus.auth.apple.AppleAuthProvider; +import com.dnd.runus.auth.token.TokenProviderModule; +import com.dnd.runus.auth.token.dto.AuthTokenDto; +import com.dnd.runus.domain.login.dto.request.LoginRequest; +import com.dnd.runus.domain.login.dto.response.TokenResponse; +import com.dnd.runus.domain.member.entity.Member; +import com.dnd.runus.domain.member.entity.PersonalProfile; +import com.dnd.runus.domain.member.entity.SocialProfile; +import com.dnd.runus.domain.member.repository.MemberRepository; +import com.dnd.runus.domain.member.repository.SocialProfileRepository; +import com.dnd.runus.global.constant.MemberRole; +import com.dnd.runus.global.constant.SocialType; +import com.dnd.runus.global.exception.BusinessException; +import com.dnd.runus.global.exception.type.ErrorType; +import io.jsonwebtoken.Claims; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +@RequiredArgsConstructor +public class LoginService { + + private final AppleAuthProvider provider; + private final TokenProviderModule tokenProviderModule; + + private final MemberRepository memberRepository; + private final SocialProfileRepository socialProfileRepository; + + /** + * 회원가입 유뮤 확인 후 회원가입/로그인 진행 + * + * @param request 로그인 request + * @return TokenResponse + */ + @Transactional + public TokenResponse login(LoginRequest request) { + + Claims claim = provider.getClaimsBy(request.idToken()); + String oAuthId = claim.getSubject(); + String email = String.valueOf(claim.get("email")); + + // 회원 가입 안되있으면 회원가입 진행 + if (!isLogin(request.socialType(), oAuthId)) { + crateMember(oAuthId, email, request.socialType(), request.nickName()); + } + + SocialProfile socialProfile = socialProfileRepository + .findBySocialTypeAndOauthId(request.socialType(), oAuthId) + .orElseThrow(IllegalStateException::new); + + // 이메일 변경(사용자가 애플의 이메일을 변경한 후 로그인하면 해당 이메일 변경해줘야함. -> 리젝 사유 될 수 있음) + if (!email.equals(socialProfile.getOauthEmail())) { + socialProfile.updateEmail(email); + } + + AuthTokenDto tokenDto = tokenProviderModule.generate(String.valueOf(socialProfile.getMemberId())); + + return TokenResponse.from(tokenDto); + } + + private boolean isLogin(SocialType socialType, String authId) { + return socialProfileRepository.existsBySocialTypeAndOauthId(socialType, authId); + } + + private void crateMember(String authId, String email, SocialType socialType, String nickName) { + if (socialProfileRepository.existsByOauthEmail(email)) { + throw new BusinessException(ErrorType.VIOLATION_OCCURRED, "이미 존재하는 이메일"); + } + + // todo 체중 디폴트는 클라이언트에서 설정해서 값만 넘겨 줄지, 백단에서 저장할지 상의 후 결정 예정 + // 현재는 들어갈 때 임시로 70이 들어가도록 하드 코딩해둠 + Long memberId = memberRepository + .save(Member.of( + MemberRole.USER, + nickName, + PersonalProfile.builder().weightKg(70).build())) + .getId(); + + socialProfileRepository.save(SocialProfile.of(socialType, authId, email, memberId)); + } +} diff --git a/src/main/java/com/dnd/runus/domain/member/entity/SocialProfile.java b/src/main/java/com/dnd/runus/domain/member/entity/SocialProfile.java index a68207ed..b52e219c 100644 --- a/src/main/java/com/dnd/runus/domain/member/entity/SocialProfile.java +++ b/src/main/java/com/dnd/runus/domain/member/entity/SocialProfile.java @@ -39,4 +39,9 @@ public static SocialProfile of(SocialType socialType, String oauthId, String oau socialProfile.memberId = memberId; return socialProfile; } + + public void updateEmail(String email) { + System.out.println("이메일"); + this.oauthEmail = email; + } } diff --git a/src/main/java/com/dnd/runus/domain/member/repository/SocialProfileRepository.java b/src/main/java/com/dnd/runus/domain/member/repository/SocialProfileRepository.java new file mode 100644 index 00000000..8b18d031 --- /dev/null +++ b/src/main/java/com/dnd/runus/domain/member/repository/SocialProfileRepository.java @@ -0,0 +1,15 @@ +package com.dnd.runus.domain.member.repository; + +import com.dnd.runus.domain.member.entity.SocialProfile; +import com.dnd.runus.global.constant.SocialType; +import org.springframework.data.jpa.repository.JpaRepository; + +import java.util.Optional; + +public interface SocialProfileRepository extends JpaRepository { + Optional findBySocialTypeAndOauthId(SocialType socialType, String oauthId); + + boolean existsByOauthEmail(String email); + + boolean existsBySocialTypeAndOauthId(SocialType socialType, String oauthId); +} From bfcf1ba97ac8f2f09fbde1a17097bdddc6150ff7 Mon Sep 17 00:00:00 2001 From: hee9841 Date: Sat, 3 Aug 2024 13:46:08 +0900 Subject: [PATCH 05/19] =?UTF-8?q?Feat:=20=EB=A1=9C=EA=B7=B8=EC=9D=B8=20?= =?UTF-8?q?=EB=B0=8F=20=ED=9A=8C=EC=9B=90=EA=B0=80=EC=9E=85=20=EC=BB=A8?= =?UTF-8?q?=ED=8A=B8=EB=A1=A4=EB=9F=AC=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../login/controller/LoginController.java | 24 +++++++++++++++++++ .../config/SecurityWebConfig.java | 2 +- 2 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 src/main/java/com/dnd/runus/domain/login/controller/LoginController.java diff --git a/src/main/java/com/dnd/runus/domain/login/controller/LoginController.java b/src/main/java/com/dnd/runus/domain/login/controller/LoginController.java new file mode 100644 index 00000000..b6bc3b11 --- /dev/null +++ b/src/main/java/com/dnd/runus/domain/login/controller/LoginController.java @@ -0,0 +1,24 @@ +package com.dnd.runus.domain.login.controller; + +import com.dnd.runus.domain.login.dto.request.LoginRequest; +import com.dnd.runus.domain.login.dto.response.TokenResponse; +import com.dnd.runus.domain.login.service.LoginService; +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RequestMapping("/api/v1/login") +@RestController +@RequiredArgsConstructor +public class LoginController { + + private final LoginService loginService; + + @PostMapping("/") + public TokenResponse signUp(@Valid @RequestBody LoginRequest request) { + return loginService.login(request); + } +} diff --git a/src/main/java/com/dnd/runus/presentation/config/SecurityWebConfig.java b/src/main/java/com/dnd/runus/presentation/config/SecurityWebConfig.java index da2e4d60..4a1256d2 100644 --- a/src/main/java/com/dnd/runus/presentation/config/SecurityWebConfig.java +++ b/src/main/java/com/dnd/runus/presentation/config/SecurityWebConfig.java @@ -33,7 +33,7 @@ public class SecurityWebConfig { private List corsOrigins; private static final String[] PUBLIC_ENDPOINTS = { - "/api/v1/auth/**", + "/api/v1/auth/**", "/api/v1/login/**", }; private static final String[] OPEN_API_ENDPOINTS = { From d30f4ce3bbc5b4223a28ce33b97718282b64a7ed Mon Sep 17 00:00:00 2001 From: hee9841 Date: Sun, 4 Aug 2024 23:28:08 +0900 Subject: [PATCH 06/19] =?UTF-8?q?Refactor:=20=EC=95=A0=ED=94=8C=20?= =?UTF-8?q?=EB=A1=9C=EA=B7=B8=EC=9D=B8=20=EB=B0=8F=20=ED=9A=8C=EC=9B=90?= =?UTF-8?q?=EA=B0=80=EC=9E=85=20=EC=B6=94=EC=83=81=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../auth/oidc/provider/OidcProvider.java | 26 +++++++++++ .../oidc/provider/OidcProviderFactory.java | 45 +++++++++++++++++++ .../provider}/PublicKeyProvider.java | 20 ++++----- .../provider/impl}/AppleAuthProvider.java | 27 ++++------- .../dnd/runus/client/vo/ApplePublicKey.java | 3 -- .../client/vo/ApplePublicKeyResponse.java | 18 -------- .../dnd/runus/client/vo/OidcPublicKey.java | 3 ++ .../runus/client/vo/OidcPublicKeyList.java | 17 +++++++ .../dnd/runus/client/web/AppleAuthClient.java | 4 +- .../client/web/AppleAuthClientComponent.java | 4 +- .../domain/login/service/LoginService.java | 8 ++-- .../global/exception/type/ErrorType.java | 1 + .../dnd/runus/global/util/DecodeUtils.java | 14 ++++++ 13 files changed, 132 insertions(+), 58 deletions(-) create mode 100644 src/main/java/com/dnd/runus/auth/oidc/provider/OidcProvider.java create mode 100644 src/main/java/com/dnd/runus/auth/oidc/provider/OidcProviderFactory.java rename src/main/java/com/dnd/runus/auth/{apple => oidc/provider}/PublicKeyProvider.java (62%) rename src/main/java/com/dnd/runus/auth/{apple => oidc/provider/impl}/AppleAuthProvider.java (65%) delete mode 100644 src/main/java/com/dnd/runus/client/vo/ApplePublicKey.java delete mode 100644 src/main/java/com/dnd/runus/client/vo/ApplePublicKeyResponse.java create mode 100644 src/main/java/com/dnd/runus/client/vo/OidcPublicKey.java create mode 100644 src/main/java/com/dnd/runus/client/vo/OidcPublicKeyList.java create mode 100644 src/main/java/com/dnd/runus/global/util/DecodeUtils.java diff --git a/src/main/java/com/dnd/runus/auth/oidc/provider/OidcProvider.java b/src/main/java/com/dnd/runus/auth/oidc/provider/OidcProvider.java new file mode 100644 index 00000000..d978133e --- /dev/null +++ b/src/main/java/com/dnd/runus/auth/oidc/provider/OidcProvider.java @@ -0,0 +1,26 @@ +package com.dnd.runus.auth.oidc.provider; + +import com.dnd.runus.global.exception.BusinessException; +import com.dnd.runus.global.exception.type.ErrorType; +import com.fasterxml.jackson.databind.ObjectMapper; +import io.jsonwebtoken.Claims; + +import java.io.IOException; +import java.util.Map; + +import static com.dnd.runus.global.util.DecodeUtils.decodeBase64; + +public interface OidcProvider { + + Claims getClaimsBy(String idToken); + + default Map parseHeaders(String token) { + String header = token.split("\\.")[0]; + + try { + return new ObjectMapper().readValue(decodeBase64(header), Map.class); + } catch (IOException e) { + throw new BusinessException(ErrorType.FAILED_PARSING, e.getMessage()); + } + } +} diff --git a/src/main/java/com/dnd/runus/auth/oidc/provider/OidcProviderFactory.java b/src/main/java/com/dnd/runus/auth/oidc/provider/OidcProviderFactory.java new file mode 100644 index 00000000..5b046887 --- /dev/null +++ b/src/main/java/com/dnd/runus/auth/oidc/provider/OidcProviderFactory.java @@ -0,0 +1,45 @@ +package com.dnd.runus.auth.oidc.provider; + +import com.dnd.runus.auth.oidc.provider.impl.AppleAuthProvider; +import com.dnd.runus.global.constant.SocialType; +import com.dnd.runus.global.exception.BusinessException; +import com.dnd.runus.global.exception.type.ErrorType; +import io.jsonwebtoken.Claims; +import org.springframework.stereotype.Component; + +import java.util.EnumMap; +import java.util.Map; + +import static java.util.Objects.isNull; + +@Component +public class OidcProviderFactory { + + private final Map authProviderMap; + private final AppleAuthProvider appleAuthProvider; + + public OidcProviderFactory(AppleAuthProvider appleAuthProvider) { + authProviderMap = new EnumMap<>(SocialType.class); + this.appleAuthProvider = appleAuthProvider; + + init(); + } + + private void init() { + authProviderMap.put(SocialType.APPLE, appleAuthProvider); + } + + public Claims getClaims(SocialType socialType, String idToken) { + return getProvider(socialType).getClaimsBy(idToken); + } + + private OidcProvider getProvider(SocialType socialType) { + OidcProvider oidcProvider = authProviderMap.get(socialType); + + if (isNull(oidcProvider)) { + throw new BusinessException(ErrorType.UNSUPPORTED_SOCIAL_TYPE, socialType.getValue()); + } + + return oidcProvider; + } +} diff --git a/src/main/java/com/dnd/runus/auth/apple/PublicKeyProvider.java b/src/main/java/com/dnd/runus/auth/oidc/provider/PublicKeyProvider.java similarity index 62% rename from src/main/java/com/dnd/runus/auth/apple/PublicKeyProvider.java rename to src/main/java/com/dnd/runus/auth/oidc/provider/PublicKeyProvider.java index f2f48fb6..4bd68fcf 100644 --- a/src/main/java/com/dnd/runus/auth/apple/PublicKeyProvider.java +++ b/src/main/java/com/dnd/runus/auth/oidc/provider/PublicKeyProvider.java @@ -1,7 +1,7 @@ -package com.dnd.runus.auth.apple; +package com.dnd.runus.auth.oidc.provider; -import com.dnd.runus.client.vo.ApplePublicKey; -import com.dnd.runus.client.vo.ApplePublicKeyResponse; +import com.dnd.runus.client.vo.OidcPublicKey; +import com.dnd.runus.client.vo.OidcPublicKeyList; import com.dnd.runus.global.exception.BusinessException; import com.dnd.runus.global.exception.type.ErrorType; import org.springframework.stereotype.Component; @@ -12,25 +12,25 @@ import java.security.PublicKey; import java.security.spec.InvalidKeySpecException; import java.security.spec.RSAPublicKeySpec; -import java.util.Base64; import java.util.Map; +import static com.dnd.runus.global.util.DecodeUtils.decodeBase64; + @Component public class PublicKeyProvider { - public PublicKey generatePublicKey( - final Map tokenHeaders, final ApplePublicKeyResponse publicKeys) { + public PublicKey generatePublicKey(final Map tokenHeaders, final OidcPublicKeyList publicKeys) { - ApplePublicKey publicKey = publicKeys.getMatchedKeyBy(tokenHeaders.get("kid"), tokenHeaders.get("alg")); + OidcPublicKey publicKey = publicKeys.getMatchedKeyBy(tokenHeaders.get("kid"), tokenHeaders.get("alg")); return getPublicKey(publicKey); } - private PublicKey getPublicKey(final ApplePublicKey key) { + private PublicKey getPublicKey(final OidcPublicKey key) { try { - final byte[] nByte = Base64.getUrlDecoder().decode(key.n()); - final byte[] eByte = Base64.getUrlDecoder().decode(key.e()); + final byte[] nByte = decodeBase64(key.n()); + final byte[] eByte = decodeBase64(key.e()); BigInteger n = new BigInteger(1, nByte); BigInteger e = new BigInteger(1, eByte); diff --git a/src/main/java/com/dnd/runus/auth/apple/AppleAuthProvider.java b/src/main/java/com/dnd/runus/auth/oidc/provider/impl/AppleAuthProvider.java similarity index 65% rename from src/main/java/com/dnd/runus/auth/apple/AppleAuthProvider.java rename to src/main/java/com/dnd/runus/auth/oidc/provider/impl/AppleAuthProvider.java index ae8632f4..017259eb 100644 --- a/src/main/java/com/dnd/runus/auth/apple/AppleAuthProvider.java +++ b/src/main/java/com/dnd/runus/auth/oidc/provider/impl/AppleAuthProvider.java @@ -1,11 +1,11 @@ -package com.dnd.runus.auth.apple; +package com.dnd.runus.auth.oidc.provider.impl; import com.dnd.runus.auth.exception.AuthException; -import com.dnd.runus.client.vo.ApplePublicKeyResponse; +import com.dnd.runus.auth.oidc.provider.OidcProvider; +import com.dnd.runus.auth.oidc.provider.PublicKeyProvider; +import com.dnd.runus.client.vo.OidcPublicKeyList; import com.dnd.runus.client.web.AppleAuthClient; -import com.dnd.runus.global.exception.BusinessException; import com.dnd.runus.global.exception.type.ErrorType; -import com.fasterxml.jackson.databind.ObjectMapper; import io.jsonwebtoken.Claims; import io.jsonwebtoken.ExpiredJwtException; import io.jsonwebtoken.Jwts; @@ -14,37 +14,26 @@ import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Component; -import java.io.IOException; import java.security.PublicKey; -import java.util.Base64; -import java.util.Map; @Component @RequiredArgsConstructor -public class AppleAuthProvider { +public class AppleAuthProvider implements OidcProvider { private final AppleAuthClient appleAuthClient; private final PublicKeyProvider publicKeyProvider; + @Override public Claims getClaimsBy(String identityToken) { // 퍼블릭 키 리스트 - ApplePublicKeyResponse publicKeys = appleAuthClient.getPublicKeys(); + OidcPublicKeyList publicKeys = appleAuthClient.getPublicKeys(); // 토큰 헤더에서 디코딩 -> 퍼블릭 키 리스트 대조회 n,e갑 디코딩 후 퍼블릭 키 생성 PublicKey publicKey = publicKeyProvider.generatePublicKey(parseHeaders(identityToken), publicKeys); return parseClaims(identityToken, publicKey); } - private Map parseHeaders(String token) { - String header = token.split("\\.")[0]; - try { - return new ObjectMapper().readValue(Base64.getUrlDecoder().decode(header), Map.class); - } catch (IOException e) { - throw new BusinessException(ErrorType.FAILED_PARSING, e.getMessage()); - } - } - - private Claims parseClaims(String token, PublicKey publicKey) { + public Claims parseClaims(String token, PublicKey publicKey) { try { return Jwts.parser() .verifyWith(publicKey) diff --git a/src/main/java/com/dnd/runus/client/vo/ApplePublicKey.java b/src/main/java/com/dnd/runus/client/vo/ApplePublicKey.java deleted file mode 100644 index a682e103..00000000 --- a/src/main/java/com/dnd/runus/client/vo/ApplePublicKey.java +++ /dev/null @@ -1,3 +0,0 @@ -package com.dnd.runus.client.vo; - -public record ApplePublicKey(String kty, String kid, String use, String alg, String n, String e) {} diff --git a/src/main/java/com/dnd/runus/client/vo/ApplePublicKeyResponse.java b/src/main/java/com/dnd/runus/client/vo/ApplePublicKeyResponse.java deleted file mode 100644 index 194c87fb..00000000 --- a/src/main/java/com/dnd/runus/client/vo/ApplePublicKeyResponse.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.dnd.runus.client.vo; - - -import com.dnd.runus.global.exception.BusinessException; -import com.dnd.runus.global.exception.type.ErrorType; -import java.util.List; - -public record ApplePublicKeyResponse(List keys) { - - public ApplePublicKey getMatchedKeyBy(String kid, String alg) { - //kid, alg 일치한 퍼블릭 키 - return keys.stream() - .filter(key -> key.kid().equals(kid) && key.alg().equals(alg)) - .findAny() - .orElseThrow(() -> new BusinessException(ErrorType.MALFORMED_ACCESS_TOKEN, "유효하지 않은 아이디입니다.")); - } - -} diff --git a/src/main/java/com/dnd/runus/client/vo/OidcPublicKey.java b/src/main/java/com/dnd/runus/client/vo/OidcPublicKey.java new file mode 100644 index 00000000..ae350843 --- /dev/null +++ b/src/main/java/com/dnd/runus/client/vo/OidcPublicKey.java @@ -0,0 +1,3 @@ +package com.dnd.runus.client.vo; + +public record OidcPublicKey(String kty, String kid, String use, String alg, String n, String e) {} diff --git a/src/main/java/com/dnd/runus/client/vo/OidcPublicKeyList.java b/src/main/java/com/dnd/runus/client/vo/OidcPublicKeyList.java new file mode 100644 index 00000000..9700ea9e --- /dev/null +++ b/src/main/java/com/dnd/runus/client/vo/OidcPublicKeyList.java @@ -0,0 +1,17 @@ +package com.dnd.runus.client.vo; + +import com.dnd.runus.global.exception.BusinessException; +import com.dnd.runus.global.exception.type.ErrorType; + +import java.util.List; + +public record OidcPublicKeyList(List keys) { + + public OidcPublicKey getMatchedKeyBy(String kid, String alg) { + // kid, alg 일치한 퍼블릭 키 + return keys.stream() + .filter(key -> key.kid().equals(kid) && key.alg().equals(alg)) + .findAny() + .orElseThrow(() -> new BusinessException(ErrorType.MALFORMED_ACCESS_TOKEN, "검증할 수 없는 토큰입니다.")); + } +} diff --git a/src/main/java/com/dnd/runus/client/web/AppleAuthClient.java b/src/main/java/com/dnd/runus/client/web/AppleAuthClient.java index 872dd1bd..06503d23 100644 --- a/src/main/java/com/dnd/runus/client/web/AppleAuthClient.java +++ b/src/main/java/com/dnd/runus/client/web/AppleAuthClient.java @@ -1,6 +1,6 @@ package com.dnd.runus.client.web; -import com.dnd.runus.client.vo.ApplePublicKeyResponse; +import com.dnd.runus.client.vo.OidcPublicKeyList; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Component; @@ -10,7 +10,7 @@ public class AppleAuthClient { private final AppleAuthClientComponent appleAuthClientComponent; - public ApplePublicKeyResponse getPublicKeys() { + public OidcPublicKeyList getPublicKeys() { return appleAuthClientComponent.getPublicKeys(); } } diff --git a/src/main/java/com/dnd/runus/client/web/AppleAuthClientComponent.java b/src/main/java/com/dnd/runus/client/web/AppleAuthClientComponent.java index 631fcb70..3b2151a5 100644 --- a/src/main/java/com/dnd/runus/client/web/AppleAuthClientComponent.java +++ b/src/main/java/com/dnd/runus/client/web/AppleAuthClientComponent.java @@ -1,6 +1,6 @@ package com.dnd.runus.client.web; -import com.dnd.runus.client.vo.ApplePublicKeyResponse; +import com.dnd.runus.client.vo.OidcPublicKeyList; import org.springframework.stereotype.Component; import org.springframework.web.service.annotation.GetExchange; import org.springframework.web.service.annotation.HttpExchange; @@ -9,5 +9,5 @@ @HttpExchange public interface AppleAuthClientComponent { @GetExchange("/keys") - ApplePublicKeyResponse getPublicKeys(); + OidcPublicKeyList getPublicKeys(); } diff --git a/src/main/java/com/dnd/runus/domain/login/service/LoginService.java b/src/main/java/com/dnd/runus/domain/login/service/LoginService.java index 1499421a..3e8481c1 100644 --- a/src/main/java/com/dnd/runus/domain/login/service/LoginService.java +++ b/src/main/java/com/dnd/runus/domain/login/service/LoginService.java @@ -1,6 +1,6 @@ package com.dnd.runus.domain.login.service; -import com.dnd.runus.auth.apple.AppleAuthProvider; +import com.dnd.runus.auth.oidc.provider.OidcProviderFactory; import com.dnd.runus.auth.token.TokenProviderModule; import com.dnd.runus.auth.token.dto.AuthTokenDto; import com.dnd.runus.domain.login.dto.request.LoginRequest; @@ -23,7 +23,7 @@ @RequiredArgsConstructor public class LoginService { - private final AppleAuthProvider provider; + private final OidcProviderFactory oidcProviderFactory; private final TokenProviderModule tokenProviderModule; private final MemberRepository memberRepository; @@ -38,7 +38,7 @@ public class LoginService { @Transactional public TokenResponse login(LoginRequest request) { - Claims claim = provider.getClaimsBy(request.idToken()); + Claims claim = oidcProviderFactory.getClaims(request.socialType(), request.idToken()); String oAuthId = claim.getSubject(); String email = String.valueOf(claim.get("email")); @@ -70,7 +70,7 @@ private void crateMember(String authId, String email, SocialType socialType, Str throw new BusinessException(ErrorType.VIOLATION_OCCURRED, "이미 존재하는 이메일"); } - // todo 체중 디폴트는 클라이언트에서 설정해서 값만 넘겨 줄지, 백단에서 저장할지 상의 후 결정 예정 + // todo 체중 디폴트는 온보딩으로 // 현재는 들어갈 때 임시로 70이 들어가도록 하드 코딩해둠 Long memberId = memberRepository .save(Member.of( diff --git a/src/main/java/com/dnd/runus/global/exception/type/ErrorType.java b/src/main/java/com/dnd/runus/global/exception/type/ErrorType.java index 864599ff..42a0c4ff 100644 --- a/src/main/java/com/dnd/runus/global/exception/type/ErrorType.java +++ b/src/main/java/com/dnd/runus/global/exception/type/ErrorType.java @@ -26,6 +26,7 @@ public enum ErrorType { MALFORMED_ACCESS_TOKEN(UNAUTHORIZED, "AUTH_004", "잘못된 형식의 토큰입니다"), TAMPERED_ACCESS_TOKEN(UNAUTHORIZED, "AUTH_005", "변조된 토큰입니다"), UNSUPPORTED_JWT_TOKEN(UNAUTHORIZED, "AUTH_006", "지원하지 않는 JWT 토큰입니다"), + UNSUPPORTED_SOCIAL_TYPE(UNAUTHORIZED, "AUTH_007", "지원하지 않는 소셜 타입입니다."), // DatabaseErrorType ENTITY_NOT_FOUND(NOT_FOUND, "DB_001", "해당 엔티티를 찾을 수 없습니다"), diff --git a/src/main/java/com/dnd/runus/global/util/DecodeUtils.java b/src/main/java/com/dnd/runus/global/util/DecodeUtils.java new file mode 100644 index 00000000..23b40274 --- /dev/null +++ b/src/main/java/com/dnd/runus/global/util/DecodeUtils.java @@ -0,0 +1,14 @@ +package com.dnd.runus.global.util; + +import java.util.Base64; + +public class DecodeUtils { + + private DecodeUtils() { + throw new IllegalStateException("util class"); + } + + public static byte[] decodeBase64(String string) { + return Base64.getUrlDecoder().decode(string); + } +} From 0b7917acac0b7e43569330660a121a0ac1c6acbb Mon Sep 17 00:00:00 2001 From: hee9841 Date: Sun, 4 Aug 2024 23:29:07 +0900 Subject: [PATCH 07/19] =?UTF-8?q?Chore:=20apple=20auth=20url=20env=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/.env.example | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/resources/.env.example b/src/main/resources/.env.example index b4ef03b7..cbc719c6 100644 --- a/src/main/resources/.env.example +++ b/src/main/resources/.env.example @@ -8,3 +8,5 @@ ALLOW_ORIGINS= ACCESS_TOKEN_EXPIRATION= ACCESS_TOKEN_SECRET_KEY= + +APPLE_AUTH_URL= From 19586a6c3d64cd01e68735edabe9702ea9777a40 Mon Sep 17 00:00:00 2001 From: hee9841 Date: Mon, 5 Aug 2024 00:07:16 +0900 Subject: [PATCH 08/19] =?UTF-8?q?Refactor:=20=EB=A1=9C=EA=B7=B8=EC=9D=B8?= =?UTF-8?q?=20url=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../{LoginController.java => SignController.java} | 6 +++--- .../dnd/runus/presentation/config/SecurityWebConfig.java | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) rename src/main/java/com/dnd/runus/domain/login/controller/{LoginController.java => SignController.java} (89%) diff --git a/src/main/java/com/dnd/runus/domain/login/controller/LoginController.java b/src/main/java/com/dnd/runus/domain/login/controller/SignController.java similarity index 89% rename from src/main/java/com/dnd/runus/domain/login/controller/LoginController.java rename to src/main/java/com/dnd/runus/domain/login/controller/SignController.java index b6bc3b11..a09d0abe 100644 --- a/src/main/java/com/dnd/runus/domain/login/controller/LoginController.java +++ b/src/main/java/com/dnd/runus/domain/login/controller/SignController.java @@ -10,14 +10,14 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; -@RequestMapping("/api/v1/login") +@RequestMapping("/api/v1/sign") @RestController @RequiredArgsConstructor -public class LoginController { +public class SignController { private final LoginService loginService; - @PostMapping("/") + @PostMapping("/in") public TokenResponse signUp(@Valid @RequestBody LoginRequest request) { return loginService.login(request); } diff --git a/src/main/java/com/dnd/runus/presentation/config/SecurityWebConfig.java b/src/main/java/com/dnd/runus/presentation/config/SecurityWebConfig.java index 4a1256d2..8474b706 100644 --- a/src/main/java/com/dnd/runus/presentation/config/SecurityWebConfig.java +++ b/src/main/java/com/dnd/runus/presentation/config/SecurityWebConfig.java @@ -33,7 +33,7 @@ public class SecurityWebConfig { private List corsOrigins; private static final String[] PUBLIC_ENDPOINTS = { - "/api/v1/auth/**", "/api/v1/login/**", + "/api/v1/auth/**", "/api/v1/sign/**", }; private static final String[] OPEN_API_ENDPOINTS = { From 3c2e445853b7be553b24967b7f68077a41940dbb Mon Sep 17 00:00:00 2001 From: sungHeeLee <70899677+hee9841@users.noreply.github.com> Date: Sun, 4 Aug 2024 23:39:48 +0900 Subject: [PATCH 09/19] =?UTF-8?q?Fix:=20postgis=20database-platform=20depr?= =?UTF-8?q?ecated=20=EC=A0=9C=EA=B1=B0=20(#36)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Chore: database-platform deprecated 이슈로 변경 * Fix: database-platform, database 설정 삭제 --- src/main/resources/application.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 90a20eaa..b9647953 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -3,8 +3,6 @@ spring.profiles.active: local spring: jpa: - database: postgresql - database-platform: org.hibernate.spatial.dialect.postgis.PostgisPG95Dialect hibernate: ddl-auto: none show-sql: true From ddcc837afb81fafc312ad379733139b01019dbfe Mon Sep 17 00:00:00 2001 From: hee9841 Date: Mon, 5 Aug 2024 00:20:54 +0900 Subject: [PATCH 10/19] Fix: Map unchecked or unsafe operations fix --- .../java/com/dnd/runus/auth/oidc/provider/OidcProvider.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/dnd/runus/auth/oidc/provider/OidcProvider.java b/src/main/java/com/dnd/runus/auth/oidc/provider/OidcProvider.java index d978133e..b4f9badd 100644 --- a/src/main/java/com/dnd/runus/auth/oidc/provider/OidcProvider.java +++ b/src/main/java/com/dnd/runus/auth/oidc/provider/OidcProvider.java @@ -2,6 +2,7 @@ import com.dnd.runus.global.exception.BusinessException; import com.dnd.runus.global.exception.type.ErrorType; +import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; import io.jsonwebtoken.Claims; @@ -18,7 +19,7 @@ default Map parseHeaders(String token) { String header = token.split("\\.")[0]; try { - return new ObjectMapper().readValue(decodeBase64(header), Map.class); + return new ObjectMapper().readValue(decodeBase64(header), new TypeReference<>() {}); } catch (IOException e) { throw new BusinessException(ErrorType.FAILED_PARSING, e.getMessage()); } From 353532b1e0f6bb8b5da3e6b6fce8b6b57f24cf55 Mon Sep 17 00:00:00 2001 From: hee9841 Date: Mon, 5 Aug 2024 00:28:29 +0900 Subject: [PATCH 11/19] Revert "Fix: Map unchecked or unsafe operations fix" This reverts commit ddcc837afb81fafc312ad379733139b01019dbfe. --- .../java/com/dnd/runus/auth/oidc/provider/OidcProvider.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/com/dnd/runus/auth/oidc/provider/OidcProvider.java b/src/main/java/com/dnd/runus/auth/oidc/provider/OidcProvider.java index b4f9badd..d978133e 100644 --- a/src/main/java/com/dnd/runus/auth/oidc/provider/OidcProvider.java +++ b/src/main/java/com/dnd/runus/auth/oidc/provider/OidcProvider.java @@ -2,7 +2,6 @@ import com.dnd.runus.global.exception.BusinessException; import com.dnd.runus.global.exception.type.ErrorType; -import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; import io.jsonwebtoken.Claims; @@ -19,7 +18,7 @@ default Map parseHeaders(String token) { String header = token.split("\\.")[0]; try { - return new ObjectMapper().readValue(decodeBase64(header), new TypeReference<>() {}); + return new ObjectMapper().readValue(decodeBase64(header), Map.class); } catch (IOException e) { throw new BusinessException(ErrorType.FAILED_PARSING, e.getMessage()); } From 03e42c14b6689c73099b7ed2d944f9b5560ac94e Mon Sep 17 00:00:00 2001 From: hee9841 Date: Mon, 5 Aug 2024 00:28:48 +0900 Subject: [PATCH 12/19] =?UTF-8?q?Revert=20"Fix:=20postgis=20database-platf?= =?UTF-8?q?orm=20deprecated=20=EC=A0=9C=EA=B1=B0=20(#36)"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 3c2e445853b7be553b24967b7f68077a41940dbb. --- src/main/resources/application.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index b9647953..90a20eaa 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -3,6 +3,8 @@ spring.profiles.active: local spring: jpa: + database: postgresql + database-platform: org.hibernate.spatial.dialect.postgis.PostgisPG95Dialect hibernate: ddl-auto: none show-sql: true From 5582e2cb3c49d42beecf376cf9c19a6a6c072016 Mon Sep 17 00:00:00 2001 From: hee9841 Date: Mon, 5 Aug 2024 00:30:00 +0900 Subject: [PATCH 13/19] Fix: Map unchecked or unsafe operations fix --- .../java/com/dnd/runus/auth/oidc/provider/OidcProvider.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/dnd/runus/auth/oidc/provider/OidcProvider.java b/src/main/java/com/dnd/runus/auth/oidc/provider/OidcProvider.java index d978133e..b4f9badd 100644 --- a/src/main/java/com/dnd/runus/auth/oidc/provider/OidcProvider.java +++ b/src/main/java/com/dnd/runus/auth/oidc/provider/OidcProvider.java @@ -2,6 +2,7 @@ import com.dnd.runus.global.exception.BusinessException; import com.dnd.runus.global.exception.type.ErrorType; +import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; import io.jsonwebtoken.Claims; @@ -18,7 +19,7 @@ default Map parseHeaders(String token) { String header = token.split("\\.")[0]; try { - return new ObjectMapper().readValue(decodeBase64(header), Map.class); + return new ObjectMapper().readValue(decodeBase64(header), new TypeReference<>() {}); } catch (IOException e) { throw new BusinessException(ErrorType.FAILED_PARSING, e.getMessage()); } From 4cc85fa2769c27dd536afb1b4aee07fad247aebe Mon Sep 17 00:00:00 2001 From: hee9841 Date: Mon, 5 Aug 2024 00:48:54 +0900 Subject: [PATCH 14/19] =?UTF-8?q?Fix:=20=EC=95=A0=ED=94=8C=20=ED=8D=BC?= =?UTF-8?q?=ED=94=8C=EB=A6=AD=20=ED=82=A4=20url=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - contextLoads Error fix --- src/test/resources/application.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/test/resources/application.yml b/src/test/resources/application.yml index 5dd78e41..248fef31 100644 --- a/src/test/resources/application.yml +++ b/src/test/resources/application.yml @@ -18,3 +18,7 @@ app: access: expiration: PT1H secret-key: test-long-long-long-secret-key + +oauth: + apple: + public-key-url: test-apple-url From 3c1d95f3ef2940bc053d083819e7abf3f73fe04b Mon Sep 17 00:00:00 2001 From: hee9841 Date: Mon, 5 Aug 2024 01:13:30 +0900 Subject: [PATCH 15/19] =?UTF-8?q?Fix:=20=EC=BD=94=EB=A9=98=ED=8A=B8=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=EC=82=AC=ED=95=AD=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/application.yml | 2 +- src/test/resources/application.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index b9647953..8e3ec055 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -25,7 +25,7 @@ app: oauth: apple: - public-key-url: ${APPLE_AUTH_URL} + public-key-url: https://appleid.apple.com/auth --- spring.config.activate.on-profile: local diff --git a/src/test/resources/application.yml b/src/test/resources/application.yml index a1644f38..1a59b834 100644 --- a/src/test/resources/application.yml +++ b/src/test/resources/application.yml @@ -20,4 +20,4 @@ app: oauth: apple: - public-key-url: test-apple-url + public-key-url: https://appleid.apple.com/auth From 05fd88be22ee3641ce8176b9439417d8871a1bf1 Mon Sep 17 00:00:00 2001 From: hee9841 Date: Mon, 5 Aug 2024 01:20:05 +0900 Subject: [PATCH 16/19] =?UTF-8?q?Style:=20login=20=ED=8C=A8=ED=82=A4?= =?UTF-8?q?=EC=A7=80=20=EB=AA=85=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/{login => sign}/controller/SignController.java | 8 ++++---- .../domain/{login => sign}/dto/request/LoginRequest.java | 2 +- .../{login => sign}/dto/response/TokenResponse.java | 2 +- .../domain/{login => sign}/service/LoginService.java | 6 +++--- 4 files changed, 9 insertions(+), 9 deletions(-) rename src/main/java/com/dnd/runus/domain/{login => sign}/controller/SignController.java (73%) rename src/main/java/com/dnd/runus/domain/{login => sign}/dto/request/LoginRequest.java (88%) rename src/main/java/com/dnd/runus/domain/{login => sign}/dto/response/TokenResponse.java (87%) rename src/main/java/com/dnd/runus/domain/{login => sign}/service/LoginService.java (95%) diff --git a/src/main/java/com/dnd/runus/domain/login/controller/SignController.java b/src/main/java/com/dnd/runus/domain/sign/controller/SignController.java similarity index 73% rename from src/main/java/com/dnd/runus/domain/login/controller/SignController.java rename to src/main/java/com/dnd/runus/domain/sign/controller/SignController.java index a09d0abe..b82bbb79 100644 --- a/src/main/java/com/dnd/runus/domain/login/controller/SignController.java +++ b/src/main/java/com/dnd/runus/domain/sign/controller/SignController.java @@ -1,8 +1,8 @@ -package com.dnd.runus.domain.login.controller; +package com.dnd.runus.domain.sign.controller; -import com.dnd.runus.domain.login.dto.request.LoginRequest; -import com.dnd.runus.domain.login.dto.response.TokenResponse; -import com.dnd.runus.domain.login.service.LoginService; +import com.dnd.runus.domain.sign.dto.request.LoginRequest; +import com.dnd.runus.domain.sign.dto.response.TokenResponse; +import com.dnd.runus.domain.sign.service.LoginService; import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.PostMapping; diff --git a/src/main/java/com/dnd/runus/domain/login/dto/request/LoginRequest.java b/src/main/java/com/dnd/runus/domain/sign/dto/request/LoginRequest.java similarity index 88% rename from src/main/java/com/dnd/runus/domain/login/dto/request/LoginRequest.java rename to src/main/java/com/dnd/runus/domain/sign/dto/request/LoginRequest.java index eec456f4..d3d6a1a2 100644 --- a/src/main/java/com/dnd/runus/domain/login/dto/request/LoginRequest.java +++ b/src/main/java/com/dnd/runus/domain/sign/dto/request/LoginRequest.java @@ -1,4 +1,4 @@ -package com.dnd.runus.domain.login.dto.request; +package com.dnd.runus.domain.sign.dto.request; import com.dnd.runus.global.constant.SocialType; import jakarta.validation.constraints.Email; diff --git a/src/main/java/com/dnd/runus/domain/login/dto/response/TokenResponse.java b/src/main/java/com/dnd/runus/domain/sign/dto/response/TokenResponse.java similarity index 87% rename from src/main/java/com/dnd/runus/domain/login/dto/response/TokenResponse.java rename to src/main/java/com/dnd/runus/domain/sign/dto/response/TokenResponse.java index 7f18f882..97d873c7 100644 --- a/src/main/java/com/dnd/runus/domain/login/dto/response/TokenResponse.java +++ b/src/main/java/com/dnd/runus/domain/sign/dto/response/TokenResponse.java @@ -1,4 +1,4 @@ -package com.dnd.runus.domain.login.dto.response; +package com.dnd.runus.domain.sign.dto.response; import com.dnd.runus.auth.token.dto.AuthTokenDto; diff --git a/src/main/java/com/dnd/runus/domain/login/service/LoginService.java b/src/main/java/com/dnd/runus/domain/sign/service/LoginService.java similarity index 95% rename from src/main/java/com/dnd/runus/domain/login/service/LoginService.java rename to src/main/java/com/dnd/runus/domain/sign/service/LoginService.java index 3e8481c1..8496ddd2 100644 --- a/src/main/java/com/dnd/runus/domain/login/service/LoginService.java +++ b/src/main/java/com/dnd/runus/domain/sign/service/LoginService.java @@ -1,15 +1,15 @@ -package com.dnd.runus.domain.login.service; +package com.dnd.runus.domain.sign.service; import com.dnd.runus.auth.oidc.provider.OidcProviderFactory; import com.dnd.runus.auth.token.TokenProviderModule; import com.dnd.runus.auth.token.dto.AuthTokenDto; -import com.dnd.runus.domain.login.dto.request.LoginRequest; -import com.dnd.runus.domain.login.dto.response.TokenResponse; import com.dnd.runus.domain.member.entity.Member; import com.dnd.runus.domain.member.entity.PersonalProfile; import com.dnd.runus.domain.member.entity.SocialProfile; import com.dnd.runus.domain.member.repository.MemberRepository; import com.dnd.runus.domain.member.repository.SocialProfileRepository; +import com.dnd.runus.domain.sign.dto.request.LoginRequest; +import com.dnd.runus.domain.sign.dto.response.TokenResponse; import com.dnd.runus.global.constant.MemberRole; import com.dnd.runus.global.constant.SocialType; import com.dnd.runus.global.exception.BusinessException; From b91bd78a0344f8d3ef5bab3595263f6cabf95564 Mon Sep 17 00:00:00 2001 From: hee9841 Date: Mon, 5 Aug 2024 03:27:29 +0900 Subject: [PATCH 17/19] =?UTF-8?q?Fix=20:=20PR=20=EC=88=98=EC=A0=95=20?= =?UTF-8?q?=EC=82=AC=ED=95=AD=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - DecodeUtils 삭제 - getClaims 리팩터링 - OidcProviderFactory init 메서드 삭제 - 로그인,회원가입 관련 도메인 네이밍 변경 - 불필요한 print 제거 --- .../auth/oidc/provider/OidcProvider.java | 8 ++++--- .../oidc/provider/OidcProviderFactory.java | 19 ++++----------- .../auth/oidc/provider/PublicKeyProvider.java | 7 +++--- .../oidc/provider/impl/AppleAuthProvider.java | 10 ++++++-- .../domain/member/entity/SocialProfile.java | 1 - .../oauth/controller/OauthController.java | 24 +++++++++++++++++++ .../dto/response/TokenResponse.java | 6 ++--- .../service/OauthService.java} | 24 +++++++------------ .../sign/controller/SignController.java | 24 ------------------- .../dnd/runus/global/util/DecodeUtils.java | 14 ----------- .../config/SecurityWebConfig.java | 2 +- 11 files changed, 56 insertions(+), 83 deletions(-) create mode 100644 src/main/java/com/dnd/runus/domain/oauth/controller/OauthController.java rename src/main/java/com/dnd/runus/domain/{sign => oauth}/dto/response/TokenResponse.java (59%) rename src/main/java/com/dnd/runus/domain/{sign/service/LoginService.java => oauth/service/OauthService.java} (77%) delete mode 100644 src/main/java/com/dnd/runus/domain/sign/controller/SignController.java delete mode 100644 src/main/java/com/dnd/runus/global/util/DecodeUtils.java diff --git a/src/main/java/com/dnd/runus/auth/oidc/provider/OidcProvider.java b/src/main/java/com/dnd/runus/auth/oidc/provider/OidcProvider.java index b4f9badd..29e4f3bf 100644 --- a/src/main/java/com/dnd/runus/auth/oidc/provider/OidcProvider.java +++ b/src/main/java/com/dnd/runus/auth/oidc/provider/OidcProvider.java @@ -1,5 +1,6 @@ package com.dnd.runus.auth.oidc.provider; +import com.dnd.runus.global.constant.SocialType; import com.dnd.runus.global.exception.BusinessException; import com.dnd.runus.global.exception.type.ErrorType; import com.fasterxml.jackson.core.type.TypeReference; @@ -7,19 +8,20 @@ import io.jsonwebtoken.Claims; import java.io.IOException; +import java.util.Base64; import java.util.Map; -import static com.dnd.runus.global.util.DecodeUtils.decodeBase64; - public interface OidcProvider { + SocialType getSocialType(); + Claims getClaimsBy(String idToken); default Map parseHeaders(String token) { String header = token.split("\\.")[0]; try { - return new ObjectMapper().readValue(decodeBase64(header), new TypeReference<>() {}); + return new ObjectMapper().readValue(Base64.getUrlDecoder().decode(header), new TypeReference<>() {}); } catch (IOException e) { throw new BusinessException(ErrorType.FAILED_PARSING, e.getMessage()); } diff --git a/src/main/java/com/dnd/runus/auth/oidc/provider/OidcProviderFactory.java b/src/main/java/com/dnd/runus/auth/oidc/provider/OidcProviderFactory.java index 5b046887..108e01dc 100644 --- a/src/main/java/com/dnd/runus/auth/oidc/provider/OidcProviderFactory.java +++ b/src/main/java/com/dnd/runus/auth/oidc/provider/OidcProviderFactory.java @@ -1,6 +1,5 @@ package com.dnd.runus.auth.oidc.provider; -import com.dnd.runus.auth.oidc.provider.impl.AppleAuthProvider; import com.dnd.runus.global.constant.SocialType; import com.dnd.runus.global.exception.BusinessException; import com.dnd.runus.global.exception.type.ErrorType; @@ -8,6 +7,7 @@ import org.springframework.stereotype.Component; import java.util.EnumMap; +import java.util.List; import java.util.Map; import static java.util.Objects.isNull; @@ -16,30 +16,19 @@ public class OidcProviderFactory { private final Map authProviderMap; - private final AppleAuthProvider appleAuthProvider; - public OidcProviderFactory(AppleAuthProvider appleAuthProvider) { + public OidcProviderFactory(List providers) { authProviderMap = new EnumMap<>(SocialType.class); - this.appleAuthProvider = appleAuthProvider; - - init(); - } - - private void init() { - authProviderMap.put(SocialType.APPLE, appleAuthProvider); + providers.forEach(provider -> authProviderMap.put(provider.getSocialType(), provider)); } public Claims getClaims(SocialType socialType, String idToken) { - return getProvider(socialType).getClaimsBy(idToken); - } - - private OidcProvider getProvider(SocialType socialType) { OidcProvider oidcProvider = authProviderMap.get(socialType); if (isNull(oidcProvider)) { throw new BusinessException(ErrorType.UNSUPPORTED_SOCIAL_TYPE, socialType.getValue()); } - return oidcProvider; + return oidcProvider.getClaimsBy(idToken); } } diff --git a/src/main/java/com/dnd/runus/auth/oidc/provider/PublicKeyProvider.java b/src/main/java/com/dnd/runus/auth/oidc/provider/PublicKeyProvider.java index 4bd68fcf..43ce3df6 100644 --- a/src/main/java/com/dnd/runus/auth/oidc/provider/PublicKeyProvider.java +++ b/src/main/java/com/dnd/runus/auth/oidc/provider/PublicKeyProvider.java @@ -12,10 +12,9 @@ import java.security.PublicKey; import java.security.spec.InvalidKeySpecException; import java.security.spec.RSAPublicKeySpec; +import java.util.Base64; import java.util.Map; -import static com.dnd.runus.global.util.DecodeUtils.decodeBase64; - @Component public class PublicKeyProvider { @@ -29,8 +28,8 @@ public PublicKey generatePublicKey(final Map tokenHeaders, final private PublicKey getPublicKey(final OidcPublicKey key) { try { - final byte[] nByte = decodeBase64(key.n()); - final byte[] eByte = decodeBase64(key.e()); + final byte[] nByte = Base64.getUrlDecoder().decode(key.n()); + final byte[] eByte = Base64.getUrlDecoder().decode(key.e()); BigInteger n = new BigInteger(1, nByte); BigInteger e = new BigInteger(1, eByte); diff --git a/src/main/java/com/dnd/runus/auth/oidc/provider/impl/AppleAuthProvider.java b/src/main/java/com/dnd/runus/auth/oidc/provider/impl/AppleAuthProvider.java index 017259eb..38c979f4 100644 --- a/src/main/java/com/dnd/runus/auth/oidc/provider/impl/AppleAuthProvider.java +++ b/src/main/java/com/dnd/runus/auth/oidc/provider/impl/AppleAuthProvider.java @@ -5,6 +5,7 @@ import com.dnd.runus.auth.oidc.provider.PublicKeyProvider; import com.dnd.runus.client.vo.OidcPublicKeyList; import com.dnd.runus.client.web.AppleAuthClient; +import com.dnd.runus.global.constant.SocialType; import com.dnd.runus.global.exception.type.ErrorType; import io.jsonwebtoken.Claims; import io.jsonwebtoken.ExpiredJwtException; @@ -23,6 +24,11 @@ public class AppleAuthProvider implements OidcProvider { private final AppleAuthClient appleAuthClient; private final PublicKeyProvider publicKeyProvider; + @Override + public SocialType getSocialType() { + return SocialType.APPLE; + } + @Override public Claims getClaimsBy(String identityToken) { // 퍼블릭 키 리스트 @@ -33,7 +39,7 @@ public Claims getClaimsBy(String identityToken) { return parseClaims(identityToken, publicKey); } - public Claims parseClaims(String token, PublicKey publicKey) { + private Claims parseClaims(String token, PublicKey publicKey) { try { return Jwts.parser() .verifyWith(publicKey) @@ -44,7 +50,7 @@ public Claims parseClaims(String token, PublicKey publicKey) { // 토큰 서명 검증 또는 구조 문제 throw new AuthException(ErrorType.MALFORMED_ACCESS_TOKEN, e.getMessage()); } catch (ExpiredJwtException e) { - throw new AuthException(ErrorType.INVALID_ACCESS_TOKEN, e.getMessage()); + throw new AuthException(ErrorType.EXPIRED_ACCESS_TOKEN, e.getMessage()); } } } diff --git a/src/main/java/com/dnd/runus/domain/member/entity/SocialProfile.java b/src/main/java/com/dnd/runus/domain/member/entity/SocialProfile.java index b52e219c..d11f6570 100644 --- a/src/main/java/com/dnd/runus/domain/member/entity/SocialProfile.java +++ b/src/main/java/com/dnd/runus/domain/member/entity/SocialProfile.java @@ -41,7 +41,6 @@ public static SocialProfile of(SocialType socialType, String oauthId, String oau } public void updateEmail(String email) { - System.out.println("이메일"); this.oauthEmail = email; } } diff --git a/src/main/java/com/dnd/runus/domain/oauth/controller/OauthController.java b/src/main/java/com/dnd/runus/domain/oauth/controller/OauthController.java new file mode 100644 index 00000000..ed1a1d72 --- /dev/null +++ b/src/main/java/com/dnd/runus/domain/oauth/controller/OauthController.java @@ -0,0 +1,24 @@ +package com.dnd.runus.domain.oauth.controller; + +import com.dnd.runus.domain.oauth.dto.request.SignInRequest; +import com.dnd.runus.domain.oauth.dto.response.TokenResponse; +import com.dnd.runus.domain.oauth.service.OauthService; +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RequestMapping("/api/v1/auth/oauth") +@RestController +@RequiredArgsConstructor +public class OauthController { + + private final OauthService oauthService; + + @PostMapping + public TokenResponse SignIn(@Valid @RequestBody SignInRequest request) { + return oauthService.SignIn(request); + } +} diff --git a/src/main/java/com/dnd/runus/domain/sign/dto/response/TokenResponse.java b/src/main/java/com/dnd/runus/domain/oauth/dto/response/TokenResponse.java similarity index 59% rename from src/main/java/com/dnd/runus/domain/sign/dto/response/TokenResponse.java rename to src/main/java/com/dnd/runus/domain/oauth/dto/response/TokenResponse.java index 97d873c7..f31c9835 100644 --- a/src/main/java/com/dnd/runus/domain/sign/dto/response/TokenResponse.java +++ b/src/main/java/com/dnd/runus/domain/oauth/dto/response/TokenResponse.java @@ -1,4 +1,4 @@ -package com.dnd.runus.domain.sign.dto.response; +package com.dnd.runus.domain.oauth.dto.response; import com.dnd.runus.auth.token.dto.AuthTokenDto; @@ -6,11 +6,11 @@ public record TokenResponse( String accessToken, //todo refresh token 구현 되면 -// String refreshToken, + String refreshToken, String grantType ) { public static TokenResponse from(AuthTokenDto tokenDto) { - return new TokenResponse(tokenDto.accessToken(), tokenDto.type()); + return new TokenResponse(tokenDto.accessToken(), "refreshToken", tokenDto.type()); } } diff --git a/src/main/java/com/dnd/runus/domain/sign/service/LoginService.java b/src/main/java/com/dnd/runus/domain/oauth/service/OauthService.java similarity index 77% rename from src/main/java/com/dnd/runus/domain/sign/service/LoginService.java rename to src/main/java/com/dnd/runus/domain/oauth/service/OauthService.java index 8496ddd2..8334984e 100644 --- a/src/main/java/com/dnd/runus/domain/sign/service/LoginService.java +++ b/src/main/java/com/dnd/runus/domain/oauth/service/OauthService.java @@ -1,4 +1,4 @@ -package com.dnd.runus.domain.sign.service; +package com.dnd.runus.domain.oauth.service; import com.dnd.runus.auth.oidc.provider.OidcProviderFactory; import com.dnd.runus.auth.token.TokenProviderModule; @@ -8,8 +8,8 @@ import com.dnd.runus.domain.member.entity.SocialProfile; import com.dnd.runus.domain.member.repository.MemberRepository; import com.dnd.runus.domain.member.repository.SocialProfileRepository; -import com.dnd.runus.domain.sign.dto.request.LoginRequest; -import com.dnd.runus.domain.sign.dto.response.TokenResponse; +import com.dnd.runus.domain.oauth.dto.request.SignInRequest; +import com.dnd.runus.domain.oauth.dto.response.TokenResponse; import com.dnd.runus.global.constant.MemberRole; import com.dnd.runus.global.constant.SocialType; import com.dnd.runus.global.exception.BusinessException; @@ -21,7 +21,7 @@ @Service @RequiredArgsConstructor -public class LoginService { +public class OauthService { private final OidcProviderFactory oidcProviderFactory; private final TokenProviderModule tokenProviderModule; @@ -36,20 +36,16 @@ public class LoginService { * @return TokenResponse */ @Transactional - public TokenResponse login(LoginRequest request) { + public TokenResponse SignIn(SignInRequest request) { Claims claim = oidcProviderFactory.getClaims(request.socialType(), request.idToken()); String oAuthId = claim.getSubject(); String email = String.valueOf(claim.get("email")); // 회원 가입 안되있으면 회원가입 진행 - if (!isLogin(request.socialType(), oAuthId)) { - crateMember(oAuthId, email, request.socialType(), request.nickName()); - } - SocialProfile socialProfile = socialProfileRepository .findBySocialTypeAndOauthId(request.socialType(), oAuthId) - .orElseThrow(IllegalStateException::new); + .orElseGet(() -> crateMember(oAuthId, email, request.socialType(), request.nickName())); // 이메일 변경(사용자가 애플의 이메일을 변경한 후 로그인하면 해당 이메일 변경해줘야함. -> 리젝 사유 될 수 있음) if (!email.equals(socialProfile.getOauthEmail())) { @@ -61,11 +57,7 @@ public TokenResponse login(LoginRequest request) { return TokenResponse.from(tokenDto); } - private boolean isLogin(SocialType socialType, String authId) { - return socialProfileRepository.existsBySocialTypeAndOauthId(socialType, authId); - } - - private void crateMember(String authId, String email, SocialType socialType, String nickName) { + private SocialProfile crateMember(String authId, String email, SocialType socialType, String nickName) { if (socialProfileRepository.existsByOauthEmail(email)) { throw new BusinessException(ErrorType.VIOLATION_OCCURRED, "이미 존재하는 이메일"); } @@ -79,6 +71,6 @@ private void crateMember(String authId, String email, SocialType socialType, Str PersonalProfile.builder().weightKg(70).build())) .getId(); - socialProfileRepository.save(SocialProfile.of(socialType, authId, email, memberId)); + return socialProfileRepository.save(SocialProfile.of(socialType, authId, email, memberId)); } } diff --git a/src/main/java/com/dnd/runus/domain/sign/controller/SignController.java b/src/main/java/com/dnd/runus/domain/sign/controller/SignController.java deleted file mode 100644 index b82bbb79..00000000 --- a/src/main/java/com/dnd/runus/domain/sign/controller/SignController.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.dnd.runus.domain.sign.controller; - -import com.dnd.runus.domain.sign.dto.request.LoginRequest; -import com.dnd.runus.domain.sign.dto.response.TokenResponse; -import com.dnd.runus.domain.sign.service.LoginService; -import jakarta.validation.Valid; -import lombok.RequiredArgsConstructor; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; - -@RequestMapping("/api/v1/sign") -@RestController -@RequiredArgsConstructor -public class SignController { - - private final LoginService loginService; - - @PostMapping("/in") - public TokenResponse signUp(@Valid @RequestBody LoginRequest request) { - return loginService.login(request); - } -} diff --git a/src/main/java/com/dnd/runus/global/util/DecodeUtils.java b/src/main/java/com/dnd/runus/global/util/DecodeUtils.java deleted file mode 100644 index 23b40274..00000000 --- a/src/main/java/com/dnd/runus/global/util/DecodeUtils.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.dnd.runus.global.util; - -import java.util.Base64; - -public class DecodeUtils { - - private DecodeUtils() { - throw new IllegalStateException("util class"); - } - - public static byte[] decodeBase64(String string) { - return Base64.getUrlDecoder().decode(string); - } -} diff --git a/src/main/java/com/dnd/runus/presentation/config/SecurityWebConfig.java b/src/main/java/com/dnd/runus/presentation/config/SecurityWebConfig.java index 8474b706..da2e4d60 100644 --- a/src/main/java/com/dnd/runus/presentation/config/SecurityWebConfig.java +++ b/src/main/java/com/dnd/runus/presentation/config/SecurityWebConfig.java @@ -33,7 +33,7 @@ public class SecurityWebConfig { private List corsOrigins; private static final String[] PUBLIC_ENDPOINTS = { - "/api/v1/auth/**", "/api/v1/sign/**", + "/api/v1/auth/**", }; private static final String[] OPEN_API_ENDPOINTS = { From e9bd960a8521d80171fc3aa372e52f47d77090f8 Mon Sep 17 00:00:00 2001 From: hee9841 Date: Mon, 5 Aug 2024 03:34:11 +0900 Subject: [PATCH 18/19] =?UTF-8?q?Fix=20:=20=ED=9A=8C=EC=9B=90=EA=B0=80?= =?UTF-8?q?=EC=9E=85=20request=20dto=20=ED=81=B4=EB=9E=98=EC=8A=A4=20?= =?UTF-8?q?=EB=AA=85=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dnd/runus/domain/oauth/controller/OauthController.java | 4 ++-- .../LoginRequest.java => oauth/dto/request/OauthRequest.java} | 4 ++-- .../java/com/dnd/runus/domain/oauth/service/OauthService.java | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) rename src/main/java/com/dnd/runus/domain/{sign/dto/request/LoginRequest.java => oauth/dto/request/OauthRequest.java} (82%) diff --git a/src/main/java/com/dnd/runus/domain/oauth/controller/OauthController.java b/src/main/java/com/dnd/runus/domain/oauth/controller/OauthController.java index ed1a1d72..bebba706 100644 --- a/src/main/java/com/dnd/runus/domain/oauth/controller/OauthController.java +++ b/src/main/java/com/dnd/runus/domain/oauth/controller/OauthController.java @@ -1,6 +1,6 @@ package com.dnd.runus.domain.oauth.controller; -import com.dnd.runus.domain.oauth.dto.request.SignInRequest; +import com.dnd.runus.domain.oauth.dto.request.OauthRequest; import com.dnd.runus.domain.oauth.dto.response.TokenResponse; import com.dnd.runus.domain.oauth.service.OauthService; import jakarta.validation.Valid; @@ -18,7 +18,7 @@ public class OauthController { private final OauthService oauthService; @PostMapping - public TokenResponse SignIn(@Valid @RequestBody SignInRequest request) { + public TokenResponse SignIn(@Valid @RequestBody OauthRequest request) { return oauthService.SignIn(request); } } diff --git a/src/main/java/com/dnd/runus/domain/sign/dto/request/LoginRequest.java b/src/main/java/com/dnd/runus/domain/oauth/dto/request/OauthRequest.java similarity index 82% rename from src/main/java/com/dnd/runus/domain/sign/dto/request/LoginRequest.java rename to src/main/java/com/dnd/runus/domain/oauth/dto/request/OauthRequest.java index d3d6a1a2..cfa7e233 100644 --- a/src/main/java/com/dnd/runus/domain/sign/dto/request/LoginRequest.java +++ b/src/main/java/com/dnd/runus/domain/oauth/dto/request/OauthRequest.java @@ -1,4 +1,4 @@ -package com.dnd.runus.domain.sign.dto.request; +package com.dnd.runus.domain.oauth.dto.request; import com.dnd.runus.global.constant.SocialType; import jakarta.validation.constraints.Email; @@ -6,7 +6,7 @@ import jakarta.validation.constraints.NotNull; -public record LoginRequest( +public record OauthRequest( @NotNull SocialType socialType, @NotBlank diff --git a/src/main/java/com/dnd/runus/domain/oauth/service/OauthService.java b/src/main/java/com/dnd/runus/domain/oauth/service/OauthService.java index 8334984e..4a61043f 100644 --- a/src/main/java/com/dnd/runus/domain/oauth/service/OauthService.java +++ b/src/main/java/com/dnd/runus/domain/oauth/service/OauthService.java @@ -8,7 +8,7 @@ import com.dnd.runus.domain.member.entity.SocialProfile; import com.dnd.runus.domain.member.repository.MemberRepository; import com.dnd.runus.domain.member.repository.SocialProfileRepository; -import com.dnd.runus.domain.oauth.dto.request.SignInRequest; +import com.dnd.runus.domain.oauth.dto.request.OauthRequest; import com.dnd.runus.domain.oauth.dto.response.TokenResponse; import com.dnd.runus.global.constant.MemberRole; import com.dnd.runus.global.constant.SocialType; @@ -36,7 +36,7 @@ public class OauthService { * @return TokenResponse */ @Transactional - public TokenResponse SignIn(SignInRequest request) { + public TokenResponse SignIn(OauthRequest request) { Claims claim = oidcProviderFactory.getClaims(request.socialType(), request.idToken()); String oAuthId = claim.getSubject(); From 6b9f9e010a3ff7d970898e8afe079a47aaa910be Mon Sep 17 00:00:00 2001 From: hee9841 Date: Mon, 5 Aug 2024 03:35:49 +0900 Subject: [PATCH 19/19] =?UTF-8?q?Docs:=20=EB=A1=9C=EA=B7=B8=EC=9D=B8=20?= =?UTF-8?q?=EB=B0=8F=20=ED=9A=8C=EC=9B=90=EA=B0=80=EC=9E=85=20api=20?= =?UTF-8?q?=EB=AC=B8=EC=84=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../oauth/controller/OauthController.java | 9 +++++++++ .../domain/oauth/dto/request/OauthRequest.java | 18 +++++++++++++++++- .../oauth/dto/response/TokenResponse.java | 12 ++++++++++-- 3 files changed, 36 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/dnd/runus/domain/oauth/controller/OauthController.java b/src/main/java/com/dnd/runus/domain/oauth/controller/OauthController.java index bebba706..0c0fbbfc 100644 --- a/src/main/java/com/dnd/runus/domain/oauth/controller/OauthController.java +++ b/src/main/java/com/dnd/runus/domain/oauth/controller/OauthController.java @@ -3,6 +3,9 @@ import com.dnd.runus.domain.oauth.dto.request.OauthRequest; import com.dnd.runus.domain.oauth.dto.response.TokenResponse; import com.dnd.runus.domain.oauth.service.OauthService; +import com.dnd.runus.global.exception.type.ApiErrorType; +import com.dnd.runus.global.exception.type.ErrorType; +import io.swagger.v3.oas.annotations.Operation; import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.PostMapping; @@ -17,6 +20,12 @@ public class OauthController { private final OauthService oauthService; + @Operation( + summary = "회원가입 및 로그인", + description = "인증토큰(idToken)과 사용자 정보로 사용자 등록을 합니다." + + " 이메일, 사용자 이름에 대해 변경사항 있는 경우 알려 주세요!(OauthRequest 스키마를 참고해 주세요.)") + @ApiErrorType({ErrorType.UNSUPPORTED_SOCIAL_TYPE, ErrorType.MALFORMED_ACCESS_TOKEN, ErrorType.UNSUPPORTED_JWT_TOKEN + }) @PostMapping public TokenResponse SignIn(@Valid @RequestBody OauthRequest request) { return oauthService.SignIn(request); diff --git a/src/main/java/com/dnd/runus/domain/oauth/dto/request/OauthRequest.java b/src/main/java/com/dnd/runus/domain/oauth/dto/request/OauthRequest.java index cfa7e233..3c24b75e 100644 --- a/src/main/java/com/dnd/runus/domain/oauth/dto/request/OauthRequest.java +++ b/src/main/java/com/dnd/runus/domain/oauth/dto/request/OauthRequest.java @@ -1,19 +1,35 @@ package com.dnd.runus.domain.oauth.dto.request; import com.dnd.runus.global.constant.SocialType; +import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.Email; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; - +@Schema(description = "로그인 및 회원가입 요청 DTO") public record OauthRequest( + @Schema( + description = "소셜 로그인 타입", + type = "string", + example = "APPLE" + ) @NotNull SocialType socialType, + @Schema( + description = "idToken" + ) @NotBlank String idToken, + @Schema( + description = "사용자 email(최초 로그인 이후 해당 값을 애플에서 제공 안 한다고 들었는데, 만약 최초 로그인 이후 제공 안한다면 알려 주세요!)", + example = "xxxx@yyyy.com" + ) @NotBlank @Email String email, + @Schema( + description = "사용자 이름(최초 로그인 이후 해당 값을 애플에서 제공 안 한다고 들었는데, 만약 최초 로그인 이후 제공 안한다면 알려 주세요!)" + ) @NotBlank String nickName ){ diff --git a/src/main/java/com/dnd/runus/domain/oauth/dto/response/TokenResponse.java b/src/main/java/com/dnd/runus/domain/oauth/dto/response/TokenResponse.java index f31c9835..0bbd6997 100644 --- a/src/main/java/com/dnd/runus/domain/oauth/dto/response/TokenResponse.java +++ b/src/main/java/com/dnd/runus/domain/oauth/dto/response/TokenResponse.java @@ -2,11 +2,19 @@ import com.dnd.runus.auth.token.dto.AuthTokenDto; +import io.swagger.v3.oas.annotations.media.Schema; +@Schema(description = "로그인 및 회원가입 응답 DTO") public record TokenResponse( + @Schema(description = "엑세스 토큰") String accessToken, -//todo refresh token 구현 되면 - String refreshToken, + //todo refresh token 구현 되면 + @Schema(description = "리프레시토큰(아직 리프레시 토큰 구현이 아직 안되어서 발급하면 'refreshToken'으로 리턴될 거에요.") + String refreshToken, + @Schema( + description = "토큰 타입", + example = "Bearer " + ) String grantType ) {