From 5990446a167a2ce6ca988d227f905278fc2b9a29 Mon Sep 17 00:00:00 2001 From: hysong4u Date: Mon, 15 Jan 2024 00:41:37 +0900 Subject: [PATCH 1/7] =?UTF-8?q?[feat]=20#32=20=ED=86=A0=ED=81=B0=20?= =?UTF-8?q?=EC=9E=AC=EB=B0=9C=EA=B8=89=20swagger=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/member/controller/OAuthApi.java | 21 +++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/sopt/sweet/domain/member/controller/OAuthApi.java b/src/main/java/org/sopt/sweet/domain/member/controller/OAuthApi.java index e4417e8..9cc02de 100644 --- a/src/main/java/org/sopt/sweet/domain/member/controller/OAuthApi.java +++ b/src/main/java/org/sopt/sweet/domain/member/controller/OAuthApi.java @@ -1,5 +1,6 @@ package org.sopt.sweet.domain.member.controller; +import com.fasterxml.jackson.core.JsonProcessingException; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.headers.Header; @@ -46,9 +47,9 @@ ResponseEntity> kakaoLogin( @ApiResponse(responseCode = "404", content = @Content) } ) - @Operation(summary = "카카오 로그아웃") + @Operation(summary = "로그아웃") @PostMapping("/api/oauth/kakao/logout") - ResponseEntity> kakaoLogout( + ResponseEntity> logout( @Parameter( description = "authorization token에서 얻은 userId, 임의입력하면 대체됩니다.", required = true, @@ -56,4 +57,20 @@ ResponseEntity> kakaoLogout( ) @UserId Long userId ); + @ApiResponses( + value = { + @ApiResponse(responseCode = "200"), + @ApiResponse(responseCode = "404", content = @Content) + } + ) + @Operation(summary = "토큰 재발급") + @PostMapping("/api/oauth/reissue") + ResponseEntity> reissueToken( + @Parameter( + description = "만료된 access token", + required = true, + example = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9" + ) @RequestBody String accessToken + ) throws JsonProcessingException; + } From fb2448ae23213ff5d3f2b5523067a471792bbd9f Mon Sep 17 00:00:00 2001 From: hysong4u Date: Mon, 15 Jan 2024 00:42:57 +0900 Subject: [PATCH 2/7] =?UTF-8?q?[feat]=20#32=20=ED=86=A0=ED=81=B0=20?= =?UTF-8?q?=EC=9E=AC=EB=B0=9C=EA=B8=89=20API=20whiteList=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../org/sopt/sweet/global/config/auth/SecurityConfig.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/sopt/sweet/global/config/auth/SecurityConfig.java b/src/main/java/org/sopt/sweet/global/config/auth/SecurityConfig.java index 86336f8..af5060b 100644 --- a/src/main/java/org/sopt/sweet/global/config/auth/SecurityConfig.java +++ b/src/main/java/org/sopt/sweet/global/config/auth/SecurityConfig.java @@ -40,9 +40,9 @@ public class SecurityConfig { "api/v1/swagger-ui/**", "api/member/token/**", "api/oauth/kakao/login/**", - "api/oauth/kakao/logout/**", "api/opengraph", - "api/room/invitations/**" + "api/room/invitations/**", + "api/oauth/reissue/**" }; @Bean From 95f0486b4594ed02cec772568d05dceaf3f54dd3 Mon Sep 17 00:00:00 2001 From: hysong4u Date: Mon, 15 Jan 2024 00:43:57 +0900 Subject: [PATCH 3/7] =?UTF-8?q?[feat]=20#32=20JwtProvider=EC=97=90=20?= =?UTF-8?q?=EB=A9=94=EC=84=9C=EB=93=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../global/config/auth/jwt/JwtProvider.java | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/main/java/org/sopt/sweet/global/config/auth/jwt/JwtProvider.java b/src/main/java/org/sopt/sweet/global/config/auth/jwt/JwtProvider.java index 00cb0ff..4ff49a1 100644 --- a/src/main/java/org/sopt/sweet/global/config/auth/jwt/JwtProvider.java +++ b/src/main/java/org/sopt/sweet/global/config/auth/jwt/JwtProvider.java @@ -1,5 +1,7 @@ package org.sopt.sweet.global.config.auth.jwt; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; import io.jsonwebtoken.*; import io.jsonwebtoken.security.Keys; import lombok.Getter; @@ -8,9 +10,11 @@ import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; +import java.nio.charset.StandardCharsets; import java.security.Key; import java.util.Base64; import java.util.Date; +import java.util.Map; @Getter @Component @@ -22,6 +26,8 @@ public class JwtProvider { @Value("${jwt.refresh-token-expire-time}") private long REFRESH_TOKEN_EXPIRE_TIME; + private final ObjectMapper objectMapper = new ObjectMapper(); + public String getIssueToken(Long userId, boolean isAccessToken) { if (isAccessToken) return generateToken(userId, ACCESS_TOKEN_EXPIRE_TIME); else return generateToken(userId, REFRESH_TOKEN_EXPIRE_TIME); @@ -81,4 +87,15 @@ private Key getSigningKey() { String encoded = Base64.getEncoder().encodeToString(secretKey.getBytes()); return Keys.hmacShaKeyFor(encoded.getBytes()); } + + + public String decodeJwtPayloadSubject(String oldAccessToken) throws JsonProcessingException { + return objectMapper.readValue( + new String(Base64.getDecoder().decode(oldAccessToken.split("\\.")[1]), StandardCharsets.UTF_8), + Map.class + ).get("sub").toString(); + } + + + } \ No newline at end of file From 748583f970c222e2e67c36edc78d885419e50375 Mon Sep 17 00:00:00 2001 From: hysong4u Date: Mon, 15 Jan 2024 00:45:01 +0900 Subject: [PATCH 4/7] =?UTF-8?q?[feat]=20#32=20=EB=A1=9C=EA=B7=B8=EC=95=84?= =?UTF-8?q?=EC=9B=83=20controller=20=EC=88=98=EC=A0=95,=20=ED=86=A0?= =?UTF-8?q?=ED=81=B0=20=EC=9E=AC=EB=B0=9C=EA=B8=89=20API=20controller=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/member/controller/OAuthController.java | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/sopt/sweet/domain/member/controller/OAuthController.java b/src/main/java/org/sopt/sweet/domain/member/controller/OAuthController.java index c8f2024..1220f4f 100644 --- a/src/main/java/org/sopt/sweet/domain/member/controller/OAuthController.java +++ b/src/main/java/org/sopt/sweet/domain/member/controller/OAuthController.java @@ -1,8 +1,10 @@ package org.sopt.sweet.domain.member.controller; +import com.fasterxml.jackson.core.JsonProcessingException; import lombok.RequiredArgsConstructor; import org.sopt.sweet.domain.member.dto.response.KakaoUserInfoResponseDto; import org.sopt.sweet.domain.member.dto.response.MemberTokenResponseDto; +import org.sopt.sweet.domain.member.dto.response.MemberReissueTokenResponseDto; import org.sopt.sweet.domain.member.service.OAuthService; import org.sopt.sweet.global.common.SuccessResponse; import org.sopt.sweet.global.config.auth.UserId; @@ -32,11 +34,18 @@ public ResponseEntity> kakaoLogin(@RequestParam("code") Strin } - @PostMapping("/kakao/logout") - public ResponseEntity> kakaoLogout(@UserId Long userId) { - oAuthService.kakaoLogout(userId); + @PostMapping("/logout") + public ResponseEntity> logout(@UserId Long userId) { + oAuthService.logout(userId); return SuccessResponse.ok("로그아웃 성공"); } + @PostMapping("/reissue") + public ResponseEntity> reissueToken(@RequestBody String accessToken) throws JsonProcessingException { + MemberReissueTokenResponseDto memberToken = oAuthService.reissue(accessToken); + return SuccessResponse.ok(memberToken); + } + + } From d6a69c2faf6682e9e430277302c406e2ed6953e6 Mon Sep 17 00:00:00 2001 From: hysong4u Date: Mon, 15 Jan 2024 00:45:33 +0900 Subject: [PATCH 5/7] =?UTF-8?q?[feat]=20#32=20=ED=86=A0=ED=81=B0=20?= =?UTF-8?q?=EC=9E=AC=EB=B0=9C=EA=B8=89=20API=20service=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 --- .../domain/member/service/OAuthService.java | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/sopt/sweet/domain/member/service/OAuthService.java b/src/main/java/org/sopt/sweet/domain/member/service/OAuthService.java index 48b727d..237c709 100644 --- a/src/main/java/org/sopt/sweet/domain/member/service/OAuthService.java +++ b/src/main/java/org/sopt/sweet/domain/member/service/OAuthService.java @@ -1,11 +1,13 @@ package org.sopt.sweet.domain.member.service; +import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.google.gson.JsonElement; import com.google.gson.JsonParser; import lombok.RequiredArgsConstructor; import org.sopt.sweet.domain.member.constant.SocialType; import org.sopt.sweet.domain.member.dto.response.KakaoUserInfoResponseDto; +import org.sopt.sweet.domain.member.dto.response.MemberReissueTokenResponseDto; import org.sopt.sweet.domain.member.dto.response.MemberTokenResponseDto; import org.sopt.sweet.domain.member.entity.Member; import org.sopt.sweet.domain.member.entity.OAuthToken; @@ -146,15 +148,25 @@ public MemberTokenResponseDto saveToken(Long memberId) { return new MemberTokenResponseDto(accessToken, refreshToken); } + public void logout(Long memberId) { + String redisKey = "RT:" + memberId; + redisTemplate.delete(redisKey); + } - public void kakaoLogout(Long memberId) { + public MemberReissueTokenResponseDto reissue(String accessToken) throws JsonProcessingException { + Long memberId = Long.valueOf(jwtProvider.decodeJwtPayloadSubject(accessToken)); String redisKey = "RT:" + memberId; - redisTemplate.delete(redisKey); + String storedRefreshToken = redisTemplate.opsForValue().get(redisKey); + jwtProvider.validateRefreshToken(storedRefreshToken); - System.out.println("카카오 로그아웃 성공 :" + memberId); + String newAccessToken = issueNewAccessToken(memberId); + String newRefreshToken = issueNewRefreshToken(memberId); + redisTemplate.opsForValue().set(redisKey, newRefreshToken, REFRESH_TOKEN_EXPIRE_TIME, TimeUnit.SECONDS); + return MemberReissueTokenResponseDto.of(newAccessToken); } + } From 5a319e64d0e2f0a18c19bab7475693e492d7de81 Mon Sep 17 00:00:00 2001 From: hysong4u Date: Mon, 15 Jan 2024 00:45:46 +0900 Subject: [PATCH 6/7] =?UTF-8?q?[feat]=20#32=20=ED=86=A0=ED=81=B0=20?= =?UTF-8?q?=EC=9E=AC=EB=B0=9C=EA=B8=89=20API=20Dto=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../response/MemberReissueTokenResponseDto.java | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 src/main/java/org/sopt/sweet/domain/member/dto/response/MemberReissueTokenResponseDto.java diff --git a/src/main/java/org/sopt/sweet/domain/member/dto/response/MemberReissueTokenResponseDto.java b/src/main/java/org/sopt/sweet/domain/member/dto/response/MemberReissueTokenResponseDto.java new file mode 100644 index 0000000..19747bf --- /dev/null +++ b/src/main/java/org/sopt/sweet/domain/member/dto/response/MemberReissueTokenResponseDto.java @@ -0,0 +1,14 @@ +package org.sopt.sweet.domain.member.dto.response; + +import lombok.Builder; + +@Builder +public record MemberReissueTokenResponseDto( + String accessToken +) { + public static MemberReissueTokenResponseDto of(String newAccessToken) { + return MemberReissueTokenResponseDto.builder() + .accessToken(newAccessToken) + .build(); + } +} From 8e811cb2c5d8d19488363bef067e58ddac0f2044 Mon Sep 17 00:00:00 2001 From: hysong4u Date: Mon, 15 Jan 2024 13:26:49 +0900 Subject: [PATCH 7/7] =?UTF-8?q?[fix]=20#32=20import=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/org/sopt/sweet/domain/member/controller/OAuthApi.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/org/sopt/sweet/domain/member/controller/OAuthApi.java b/src/main/java/org/sopt/sweet/domain/member/controller/OAuthApi.java index f4d029d..3613d10 100644 --- a/src/main/java/org/sopt/sweet/domain/member/controller/OAuthApi.java +++ b/src/main/java/org/sopt/sweet/domain/member/controller/OAuthApi.java @@ -12,6 +12,7 @@ import org.sopt.sweet.global.config.auth.UserId; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestParam; @Tag(name = "소셜로그인", description = "소셜로그인 관련 API")