diff --git a/app/api/common-api/src/main/java/org/example/config/SecurityConfig.java b/app/api/common-api/src/main/java/org/example/config/SecurityConfig.java index 45c2fbf6..16171653 100644 --- a/app/api/common-api/src/main/java/org/example/config/SecurityConfig.java +++ b/app/api/common-api/src/main/java/org/example/config/SecurityConfig.java @@ -69,6 +69,7 @@ private RequestMatcher getMatcherForAnyone() { antMatcher("/css/**"), antMatcher("/js/**"), antMatcher(HttpMethod.POST, "/api/v1/users/login"), + antMatcher(HttpMethod.POST, "/api/v1/users/reissue"), antMatcher(HttpMethod.POST, "/admin/login"), antMatcher(HttpMethod.POST, "/admin/signup"), antMatcher(HttpMethod.GET, "/admin/home"), diff --git a/app/api/common-api/src/main/java/org/example/filter/JWTFilter.java b/app/api/common-api/src/main/java/org/example/filter/JWTFilter.java index 48640f86..93b9e0f7 100644 --- a/app/api/common-api/src/main/java/org/example/filter/JWTFilter.java +++ b/app/api/common-api/src/main/java/org/example/filter/JWTFilter.java @@ -1,6 +1,5 @@ package org.example.filter; -import com.fasterxml.jackson.databind.ObjectMapper; import jakarta.servlet.FilterChain; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServletRequest; @@ -8,9 +7,7 @@ import java.io.IOException; import java.util.List; import lombok.RequiredArgsConstructor; -import org.example.repository.TokenRepository; import org.example.security.dto.AuthenticatedUser; -import org.example.security.dto.TokenParam; import org.example.security.dto.UserParam; import org.example.security.token.JWTHandler; import org.example.security.token.TokenProcessor; @@ -26,7 +23,6 @@ public class JWTFilter extends OncePerRequestFilter { private final JWTHandler jwtHandler; private final TokenProcessor tokenProcessor; - private final TokenRepository tokenRepository; @Override protected void doFilterInternal( @@ -34,12 +30,6 @@ protected void doFilterInternal( HttpServletResponse response, FilterChain filterChain ) throws ServletException, IOException { - if (request.getHeader("Refresh") != null) { - TokenParam token = tokenProcessor.reissueToken(request); - response.getWriter().write(new ObjectMapper().writeValueAsString(token)); - return; - } - if (request.getHeader("Authorization") != null) { handleAccessToken(request); } diff --git a/app/api/common-api/src/main/java/org/example/security/token/TokenProcessor.java b/app/api/common-api/src/main/java/org/example/security/token/TokenProcessor.java index 42af7012..447d4d97 100644 --- a/app/api/common-api/src/main/java/org/example/security/token/TokenProcessor.java +++ b/app/api/common-api/src/main/java/org/example/security/token/TokenProcessor.java @@ -1,6 +1,5 @@ package org.example.security.token; -import jakarta.servlet.http.HttpServletRequest; import java.util.Date; import java.util.UUID; import lombok.RequiredArgsConstructor; @@ -19,8 +18,7 @@ public class TokenProcessor { private final JWTGenerator jwtGenerator; private final TokenRepository tokenRepository; - public TokenParam reissueToken(HttpServletRequest request) { - String refreshToken = jwtHandler.extractRefreshToken(request); + public TokenParam reissueToken(String refreshToken) { UserParam userParam = jwtHandler.extractUserFrom(refreshToken); String oldRefreshToken = getExistRefreshToken(userParam); diff --git a/app/api/user-api/src/main/java/org/example/controller/UserController.java b/app/api/user-api/src/main/java/org/example/controller/UserController.java index 0561d28c..6c947f5f 100644 --- a/app/api/user-api/src/main/java/org/example/controller/UserController.java +++ b/app/api/user-api/src/main/java/org/example/controller/UserController.java @@ -6,8 +6,10 @@ import lombok.RequiredArgsConstructor; import org.example.controller.dto.request.LoginApiRequest; import org.example.controller.dto.request.LogoutApiRequest; +import org.example.controller.dto.request.ReissueApiRequest; import org.example.controller.dto.request.WithdrawalApiRequest; import org.example.controller.dto.response.LoginApiResponse; +import org.example.controller.dto.response.ReissueApiResponse; import org.example.controller.dto.response.UserProfileApiResponse; import org.example.security.dto.AuthenticatedUser; import org.example.security.dto.TokenParam; @@ -45,7 +47,7 @@ public ResponseEntity signUp(@Valid @RequestBody LoginApiReque @Operation(summary = "로그아웃") public ResponseEntity logout( @AuthenticationPrincipal AuthenticatedUser user, - @RequestBody LogoutApiRequest request + @Valid @RequestBody LogoutApiRequest request ) { userService.logout(request.toServiceRequest(user.userId())); return ResponseEntity.noContent().build(); @@ -55,12 +57,27 @@ public ResponseEntity logout( @Operation(summary = "회원탈퇴") public ResponseEntity withdraw( @AuthenticationPrincipal AuthenticatedUser user, - @RequestBody WithdrawalApiRequest request + @Valid @RequestBody WithdrawalApiRequest request ) { userService.withdraw(request.toServiceRequest(user.userId())); return ResponseEntity.noContent().build(); } + @PostMapping("/reissue") + @Operation(summary = "토큰 재발급") + public ResponseEntity reissue( + @Valid @RequestBody ReissueApiRequest request + ) { + TokenParam reissueToken = userService.reissue(request.toServiceRequest()); + + return ResponseEntity.ok( + ReissueApiResponse.builder() + .accessToken(reissueToken.accessToken()) + .refreshToken(reissueToken.refreshToken()) + .build() + ); + } + @GetMapping("/profile") @Operation(summary = "회원 정보") public ResponseEntity profile( diff --git a/app/api/user-api/src/main/java/org/example/controller/dto/request/LogoutApiRequest.java b/app/api/user-api/src/main/java/org/example/controller/dto/request/LogoutApiRequest.java index b69dd279..cbb3fe1e 100644 --- a/app/api/user-api/src/main/java/org/example/controller/dto/request/LogoutApiRequest.java +++ b/app/api/user-api/src/main/java/org/example/controller/dto/request/LogoutApiRequest.java @@ -1,9 +1,13 @@ package org.example.controller.dto.request; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; import java.util.UUID; import org.example.service.dto.request.LogoutServiceRequest; public record LogoutApiRequest( + @Schema(description = "인증 토큰") + @NotNull(message = "accessToken은 필수 입력값입니다.") String accessToken ) { diff --git a/app/api/user-api/src/main/java/org/example/controller/dto/request/ReissueApiRequest.java b/app/api/user-api/src/main/java/org/example/controller/dto/request/ReissueApiRequest.java new file mode 100644 index 00000000..3a89a9cd --- /dev/null +++ b/app/api/user-api/src/main/java/org/example/controller/dto/request/ReissueApiRequest.java @@ -0,0 +1,16 @@ +package org.example.controller.dto.request; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import org.example.service.dto.request.ReissueServiceRequest; + +public record ReissueApiRequest( + @Schema(description = "재발급 토큰") + @NotNull(message = "refreshToken은 필수 입력값입니다.") + String refreshToken +) { + + public ReissueServiceRequest toServiceRequest() { + return new ReissueServiceRequest(refreshToken); + } +} diff --git a/app/api/user-api/src/main/java/org/example/controller/dto/request/WithdrawalApiRequest.java b/app/api/user-api/src/main/java/org/example/controller/dto/request/WithdrawalApiRequest.java index 32448c17..49432c88 100644 --- a/app/api/user-api/src/main/java/org/example/controller/dto/request/WithdrawalApiRequest.java +++ b/app/api/user-api/src/main/java/org/example/controller/dto/request/WithdrawalApiRequest.java @@ -1,9 +1,14 @@ package org.example.controller.dto.request; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; import java.util.UUID; import org.example.service.dto.request.WithdrawalServiceRequest; public record WithdrawalApiRequest( + + @Schema(description = "인증 토큰") + @NotNull(message = "accessToken은 필수 입력값입니다.") String accessToken ) { diff --git a/app/api/user-api/src/main/java/org/example/controller/dto/response/ReissueApiResponse.java b/app/api/user-api/src/main/java/org/example/controller/dto/response/ReissueApiResponse.java new file mode 100644 index 00000000..830baa6b --- /dev/null +++ b/app/api/user-api/src/main/java/org/example/controller/dto/response/ReissueApiResponse.java @@ -0,0 +1,16 @@ +package org.example.controller.dto.response; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Builder; + +@Builder +public record ReissueApiResponse( + + @Schema(description = "재발급된 액세스 토큰") + String accessToken, + + @Schema(description = "재발급된 리프레시 토큰") + String refreshToken +) { + +} diff --git a/app/api/user-api/src/main/java/org/example/service/UserService.java b/app/api/user-api/src/main/java/org/example/service/UserService.java index d971a92a..c7e238e3 100644 --- a/app/api/user-api/src/main/java/org/example/service/UserService.java +++ b/app/api/user-api/src/main/java/org/example/service/UserService.java @@ -15,6 +15,7 @@ import org.example.security.token.TokenProcessor; import org.example.service.dto.request.LoginServiceRequest; import org.example.service.dto.request.LogoutServiceRequest; +import org.example.service.dto.request.ReissueServiceRequest; import org.example.service.dto.request.WithdrawalServiceRequest; import org.example.service.dto.response.UserProfileServiceResponse; import org.example.usecase.UserUseCase; @@ -55,6 +56,10 @@ public void withdraw(WithdrawalServiceRequest request) { ); } + public TokenParam reissue(ReissueServiceRequest request) { + return tokenProcessor.reissueToken(request.refreshToken()); + } + public UserProfileServiceResponse findUserProfile(UUID userId) { UserProfileDomainResponse profile; try { diff --git a/app/api/user-api/src/main/java/org/example/service/dto/request/ReissueServiceRequest.java b/app/api/user-api/src/main/java/org/example/service/dto/request/ReissueServiceRequest.java new file mode 100644 index 00000000..bd5bed1e --- /dev/null +++ b/app/api/user-api/src/main/java/org/example/service/dto/request/ReissueServiceRequest.java @@ -0,0 +1,7 @@ +package org.example.service.dto.request; + +public record ReissueServiceRequest( + String refreshToken +) { + +}