From d01c73fa976387275e264edf4a97890c0ec22a20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AF=BC=EA=B2=B8?= <67851124+Mingyum-Kim@users.noreply.github.com> Date: Sun, 17 Sep 2023 18:06:50 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20=ED=86=A0=ED=81=B0=20=EA=B4=80=EB=A6=AC?= =?UTF-8?q?=20API=20=EC=B6=94=EA=B0=80=20(#68)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: MingyeomKim <67851124+MingyeomKim@users.noreply.github.com> --- .../com/backend/auth/jwt/TokenProvider.java | 31 ++++++++---- .../auth/jwt/filter/AuthenticationFilter.java | 1 + .../auth/presentation/OAuthController.java | 4 +- .../auth/presentation/TokenController.java | 50 +++++++++++++++++++ .../dto/request/ExpireTimeRequest.java | 27 ++++++++++ .../dto/response/ExpireTimeResponse.java | 33 ++++++++++++ .../backend/global/config/SecurityConfig.java | 1 + 7 files changed, 136 insertions(+), 11 deletions(-) create mode 100644 src/main/java/com/backend/auth/presentation/TokenController.java create mode 100644 src/main/java/com/backend/auth/presentation/dto/request/ExpireTimeRequest.java create mode 100644 src/main/java/com/backend/auth/presentation/dto/response/ExpireTimeResponse.java diff --git a/src/main/java/com/backend/auth/jwt/TokenProvider.java b/src/main/java/com/backend/auth/jwt/TokenProvider.java index ed531b1..2891baf 100644 --- a/src/main/java/com/backend/auth/jwt/TokenProvider.java +++ b/src/main/java/com/backend/auth/jwt/TokenProvider.java @@ -29,13 +29,9 @@ @Slf4j @Component public class TokenProvider { - private static final long ACCESS_TOKEN_EXPIRE_TIME = 1000 * 60 * 60 * 2; // 2시간 + private Long accessTokenExpireTime = Long.valueOf(1000 * 60 * 60 * 2); -// private static final long ACCESS_TOKEN_EXPIRE_TIME = 1000 * 30; // 30초 - - private static final long REFRESH_TOKEN_EXPIRE_TIME = 1000 * 60 * 60 * 24 * 14; // 2주 - -// private static final long REFRESH_TOKEN_EXPIRE_TIME = 1000 * 60; // 1분 + private Long refreshTokenExpireTime = Long.valueOf(1000 * 60 * 60 * 24 * 14); private static final String TOKEN_HEADER_PREFIX = "Bearer "; @@ -45,18 +41,18 @@ public class TokenProvider { private final MemberRepository memberRepository; - public TokenProvider(@Value("${jwt.secret}") String secretKey, RefreshTokenService refreshTokenService, MemberRepository memberRepository){ + public TokenProvider(@Value("${jwt.secret}") String secretKey, MemberRepository memberRepository){ this.memberRepository = memberRepository; byte[] keyBytes = Decoders.BASE64.decode(secretKey); this.key = Keys.hmacShaKeyFor(keyBytes); } public String generateAccessToken(String uid){ - return generateToken(uid, ACCESS_TOKEN_EXPIRE_TIME); + return generateToken(uid, accessTokenExpireTime); } public String generateRefreshToken(String uid){ - return generateToken(uid, REFRESH_TOKEN_EXPIRE_TIME); + return generateToken(uid, refreshTokenExpireTime); } public String generateToken(String uid, Long expireTime){ @@ -130,4 +126,21 @@ public Authentication getAuthentication(String accessToken) { UserDetails principal = new User(claims.getSubject(), "", authorities); return new UsernamePasswordAuthenticationToken(principal, "", authorities); } + + public void updateAccessTokenExpireTime(Long expireTime){ + accessTokenExpireTime = expireTime; + } + + public void updateRefreshTokenExpireTime(Long expireTime){ + refreshTokenExpireTime = expireTime; + } + + public Long getAccessTokenExpireTime(){ + return accessTokenExpireTime; + } + + public Long getRefreshTokenExpireTime() { + return refreshTokenExpireTime; + } + } diff --git a/src/main/java/com/backend/auth/jwt/filter/AuthenticationFilter.java b/src/main/java/com/backend/auth/jwt/filter/AuthenticationFilter.java index 2ba10b9..b5b1ed8 100644 --- a/src/main/java/com/backend/auth/jwt/filter/AuthenticationFilter.java +++ b/src/main/java/com/backend/auth/jwt/filter/AuthenticationFilter.java @@ -29,6 +29,7 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse try { String accessToken = tokenProvider.getToken(request.getHeader(AUTHORIZATION_HEADER)); + // 토큰의 유효성을 검증 tokenProvider.validateToken(accessToken); blackListService.checkBlackList(accessToken); diff --git a/src/main/java/com/backend/auth/presentation/OAuthController.java b/src/main/java/com/backend/auth/presentation/OAuthController.java index 2ae6199..396119b 100644 --- a/src/main/java/com/backend/auth/presentation/OAuthController.java +++ b/src/main/java/com/backend/auth/presentation/OAuthController.java @@ -1,7 +1,7 @@ package com.backend.auth.presentation; import com.backend.auth.application.OAuthService; -import com.backend.auth.application.RefreshTokenService; +import com.backend.auth.jwt.TokenProvider; import com.backend.auth.presentation.dto.request.LoginRequestDto; import com.backend.auth.presentation.dto.response.LoginResponse; import com.backend.auth.presentation.dto.response.ReissueResponse; @@ -26,7 +26,7 @@ public class OAuthController { private final OAuthService oauthService; - private final RefreshTokenService refreshTokenService; + private final TokenProvider tokenProvider; @Operation(summary = "소셜 로그인", description = "카카오, 애플 서버에서 로그인한 사용자의 userId를 통해 access token과 refresh token을 반환합니다.") diff --git a/src/main/java/com/backend/auth/presentation/TokenController.java b/src/main/java/com/backend/auth/presentation/TokenController.java new file mode 100644 index 0000000..757a3e2 --- /dev/null +++ b/src/main/java/com/backend/auth/presentation/TokenController.java @@ -0,0 +1,50 @@ +package com.backend.auth.presentation; + +import com.backend.auth.jwt.TokenProvider; +import com.backend.auth.presentation.dto.request.ExpireTimeRequest; +import com.backend.auth.presentation.dto.response.ExpireTimeResponse; +import com.backend.global.common.response.CustomResponse; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import static com.backend.global.common.code.SuccessCode.SELECT_SUCCESS; +import static com.backend.global.common.code.SuccessCode.UPDATE_SUCCESS; + +@Tag(name = "token", description = "토큰 관리 API입니다.") +@RequiredArgsConstructor +@RequestMapping("/token") +@RestController +public class TokenController { + private final TokenProvider tokenProvider; + + @Operation(summary = "액세스 토큰 유효 시간 수정", description = "액세스 토큰의 유효 시간을 수정한다.") + @PutMapping("/access/expire") + public ResponseEntity> updateAccessTokenExpireTime(@RequestBody ExpireTimeRequest expireTimeRequest){ + tokenProvider.updateAccessTokenExpireTime(expireTimeRequest.toLong()); + return CustomResponse.success(UPDATE_SUCCESS); + } + + @Operation(summary = "리프레시 토큰 유효 시간 수정", description = "리프레시 토큰의 유효 시간을 수정한다.") + @PutMapping("/refresh/expire") + public ResponseEntity> updateRefreshTokenExpireTime(@RequestBody ExpireTimeRequest expireTimeRequest){ + tokenProvider.updateRefreshTokenExpireTime(expireTimeRequest.toLong()); + return CustomResponse.success(UPDATE_SUCCESS); + } + + @Operation(summary = "액세스 토큰 유효 시간 조회", description = "액세스 토큰의 유효 시간을 초 단위로 조회한다.") + @GetMapping("/access/expire") + public ResponseEntity> getAccessTokenExpireTime(){ + return CustomResponse.success(SELECT_SUCCESS, ExpireTimeResponse.from(tokenProvider.getAccessTokenExpireTime())); + } + + @Operation(summary = "리프레시 토큰 유효 시간 조회", description = "리프레시 토큰의 유효 시간을 초 단위로 조회한다.") + @GetMapping("/refresh/expire") + public ResponseEntity> getRefreshTokenExpireTime(){ + return CustomResponse.success(SELECT_SUCCESS, ExpireTimeResponse.from(tokenProvider.getRefreshTokenExpireTime())); + } + +} diff --git a/src/main/java/com/backend/auth/presentation/dto/request/ExpireTimeRequest.java b/src/main/java/com/backend/auth/presentation/dto/request/ExpireTimeRequest.java new file mode 100644 index 0000000..fd6846a --- /dev/null +++ b/src/main/java/com/backend/auth/presentation/dto/request/ExpireTimeRequest.java @@ -0,0 +1,27 @@ +package com.backend.auth.presentation.dto.request; + +import io.swagger.v3.oas.annotations.media.Schema; + +public record ExpireTimeRequest( + @Schema(description = "만료 기한의 일수", example = "14") + Long days, + + @Schema(description = "만료 기한의 시간", example = "0") + Long hours, + + @Schema(description = "만료 기한의 분", example = "0") + Long minutes, + + @Schema(description = "만료 기한의 초", example = "0") + Long seconds +) { + public Long toLong() { + Long expireTime = 1000L; + + if(days!= 0) expireTime *= (days * 24 * 60 * 60); + if(hours != 0) expireTime *= (hours * 60 * 60); + if(minutes != 0) expireTime *= (minutes * 60); + if(seconds != 0) expireTime *= (seconds * 60); + return expireTime; + } +} diff --git a/src/main/java/com/backend/auth/presentation/dto/response/ExpireTimeResponse.java b/src/main/java/com/backend/auth/presentation/dto/response/ExpireTimeResponse.java new file mode 100644 index 0000000..d76def4 --- /dev/null +++ b/src/main/java/com/backend/auth/presentation/dto/response/ExpireTimeResponse.java @@ -0,0 +1,33 @@ +package com.backend.auth.presentation.dto.response; + +import io.swagger.v3.oas.annotations.media.Schema; + +public record ExpireTimeResponse( + @Schema(description = "만료 기한의 일수", example = "14") + Long days, + + @Schema(description = "만료 기한의 시간", example = "0") + Long hours, + + @Schema(description = "만료 기한의 분", example = "0") + Long minutes, + + @Schema(description = "만료 기한의 초", example = "0") + Long seconds +) { + public static ExpireTimeResponse from(Long expireTime) { + expireTime /= 1000; + + Long days = expireTime / (24 * 60 * 60); + if(days != 0) expireTime %= days; + + Long hours = expireTime / (60 * 60); + if(hours != 0) expireTime %= hours; + + Long minutes = expireTime / 60; + if(minutes != 0) expireTime %= minutes; + + Long seconds = expireTime / 60; + return new ExpireTimeResponse(days, hours, minutes, seconds); + } +} diff --git a/src/main/java/com/backend/global/config/SecurityConfig.java b/src/main/java/com/backend/global/config/SecurityConfig.java index d476d98..e53a860 100644 --- a/src/main/java/com/backend/global/config/SecurityConfig.java +++ b/src/main/java/com/backend/global/config/SecurityConfig.java @@ -20,6 +20,7 @@ public class SecurityConfig { private final TokenProvider tokenProvider; + private final BlackListService blackListService; private final JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;