From 2e7f2257743f07d2250318778cbf482df6a31891 Mon Sep 17 00:00:00 2001 From: seungin Date: Fri, 18 Jul 2025 19:57:53 +0900 Subject: [PATCH 1/9] =?UTF-8?q?[feat]=20refresh=20token=20=EA=B4=80?= =?UTF-8?q?=EB=A0=A8=20=EC=97=94=ED=8B=B0=ED=8B=B0=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../server/user/entity/RefreshToken.java | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 src/main/java/com/wayble/server/user/entity/RefreshToken.java diff --git a/src/main/java/com/wayble/server/user/entity/RefreshToken.java b/src/main/java/com/wayble/server/user/entity/RefreshToken.java new file mode 100644 index 00000000..7036eb76 --- /dev/null +++ b/src/main/java/com/wayble/server/user/entity/RefreshToken.java @@ -0,0 +1,26 @@ +package com.wayble.server.user.entity; + +import jakarta.persistence.*; +import lombok.*; + +@Getter +@Entity +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@AllArgsConstructor +@Builder +@Table(name = "refresh_token") +public class RefreshToken { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(nullable = false, unique = true) // 1인 1토큰 + private Long userId; + + @Column(nullable = false) + private String token; + + @Column(nullable = false) + private Long expiry; // 만료 시간 +} \ No newline at end of file From 1714b5a870e7dcdd90844285d02b74505e337e5a Mon Sep 17 00:00:00 2001 From: seungin Date: Fri, 18 Jul 2025 19:58:55 +0900 Subject: [PATCH 2/9] =?UTF-8?q?[feat]=20refresh=20token=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84=EC=9D=84=20=EC=9C=84=ED=95=9C=20=EB=A0=88=ED=8F=AC?= =?UTF-8?q?=EC=A7=80=ED=86=A0=EB=A6=AC=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../user/repository/RefreshTokenRepository.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 src/main/java/com/wayble/server/user/repository/RefreshTokenRepository.java diff --git a/src/main/java/com/wayble/server/user/repository/RefreshTokenRepository.java b/src/main/java/com/wayble/server/user/repository/RefreshTokenRepository.java new file mode 100644 index 00000000..eefa8bd2 --- /dev/null +++ b/src/main/java/com/wayble/server/user/repository/RefreshTokenRepository.java @@ -0,0 +1,12 @@ +package com.wayble.server.user.repository; + +import com.wayble.server.user.entity.RefreshToken; +import org.springframework.data.jpa.repository.JpaRepository; + +import java.util.Optional; + +public interface RefreshTokenRepository extends JpaRepository { + Optional findByUserId(Long userId); + Optional findByToken(String token); + void deleteByUserId(Long userId); +} \ No newline at end of file From f0a979c0f2ce4018f21d38b14df4126f0b0dc12c Mon Sep 17 00:00:00 2001 From: seungin Date: Fri, 18 Jul 2025 20:03:38 +0900 Subject: [PATCH 3/9] =?UTF-8?q?[feat]=20refresh=20token=20=EA=B4=80?= =?UTF-8?q?=EB=A0=A8=20=EC=83=9D=EC=84=B1=20=EB=A9=94=EC=84=9C=EB=93=9C=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 --- .../common/config/security/JwtProperties.java | 5 +++++ .../config/security/jwt/JwtTokenProvider.java | 14 ++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/src/main/java/com/wayble/server/common/config/security/JwtProperties.java b/src/main/java/com/wayble/server/common/config/security/JwtProperties.java index a1efd248..4d47c445 100644 --- a/src/main/java/com/wayble/server/common/config/security/JwtProperties.java +++ b/src/main/java/com/wayble/server/common/config/security/JwtProperties.java @@ -9,6 +9,7 @@ public class JwtProperties { private String secret; private long accessExp; + private long refreshExp; public String getSecret() { @@ -26,4 +27,8 @@ public void setSecret(String secret) { public void setAccessExp(long accessExp) { this.accessExp = accessExp; } + + public long getRefreshExp() { return refreshExp; } + + public void setRefreshExp(long refreshExp) { this.refreshExp = refreshExp; } } diff --git a/src/main/java/com/wayble/server/common/config/security/jwt/JwtTokenProvider.java b/src/main/java/com/wayble/server/common/config/security/jwt/JwtTokenProvider.java index 25227b3c..018704e6 100644 --- a/src/main/java/com/wayble/server/common/config/security/jwt/JwtTokenProvider.java +++ b/src/main/java/com/wayble/server/common/config/security/jwt/JwtTokenProvider.java @@ -31,6 +31,15 @@ public String generateToken(Long userId, String role) { .compact(); } + public String generateRefreshToken(Long userId) { + return Jwts.builder() + .setSubject(String.valueOf(userId)) + .setIssuedAt(new Date()) + .setExpiration(new Date(System.currentTimeMillis() + jwtProperties.getRefreshExp())) + .signWith(signingKey, SignatureAlgorithm.HS256) + .compact(); + } + public boolean validateToken(String token) { try { Jwts.parserBuilder().setSigningKey(signingKey).build().parseClaimsJws(token); @@ -53,4 +62,9 @@ public Long getUserId(String token) { .getSubject(); return Long.parseLong(subject); } + + public Long getTokenExpiry(String token) { + return Jwts.parserBuilder().setSigningKey(signingKey).build() + .parseClaimsJws(token).getBody().getExpiration().getTime(); + } } \ No newline at end of file From 7afaf7cb4535871ee2912b234235d7079b828412 Mon Sep 17 00:00:00 2001 From: seungin Date: Fri, 18 Jul 2025 20:06:18 +0900 Subject: [PATCH 4/9] =?UTF-8?q?[refactor]=20AuthService=EC=97=90=EC=84=9C?= =?UTF-8?q?=20=EB=A1=9C=EA=B7=B8=EC=9D=B8=20=EC=8B=9C=20access,refresh=20?= =?UTF-8?q?=EB=AA=A8=EB=91=90=20=EB=B0=9C=EA=B8=89,refreshToken=20?= =?UTF-8?q?=EC=A0=80=EC=9E=A5=ED=95=98=EB=8F=84=EB=A1=9D=20=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../user/dto/token/TokenResponseDto.java | 2 +- .../server/user/service/auth/AuthService.java | 41 ++++++++++++++++++- 2 files changed, 40 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/wayble/server/user/dto/token/TokenResponseDto.java b/src/main/java/com/wayble/server/user/dto/token/TokenResponseDto.java index 57c54dbc..f006ecc8 100644 --- a/src/main/java/com/wayble/server/user/dto/token/TokenResponseDto.java +++ b/src/main/java/com/wayble/server/user/dto/token/TokenResponseDto.java @@ -1,3 +1,3 @@ package com.wayble.server.user.dto.token; -public record TokenResponseDto(String accessToken) {} \ No newline at end of file +public record TokenResponseDto(String accessToken, String refreshToken) {} \ No newline at end of file diff --git a/src/main/java/com/wayble/server/user/service/auth/AuthService.java b/src/main/java/com/wayble/server/user/service/auth/AuthService.java index 3f9a1fa7..dd3d3a9e 100644 --- a/src/main/java/com/wayble/server/user/service/auth/AuthService.java +++ b/src/main/java/com/wayble/server/user/service/auth/AuthService.java @@ -4,8 +4,10 @@ import com.wayble.server.common.exception.ApplicationException; import com.wayble.server.user.dto.UserLoginRequestDto; import com.wayble.server.user.dto.token.TokenResponseDto; +import com.wayble.server.user.entity.RefreshToken; import com.wayble.server.user.entity.User; import com.wayble.server.user.exception.UserErrorCase; +import com.wayble.server.user.repository.RefreshTokenRepository; import com.wayble.server.user.repository.UserRepository; import lombok.RequiredArgsConstructor; import org.springframework.security.crypto.password.PasswordEncoder; @@ -16,6 +18,7 @@ public class AuthService { private final UserRepository userRepository; + private final RefreshTokenRepository refreshTokenRepository; private final PasswordEncoder encoder; private final JwtTokenProvider jwtProvider; @@ -25,7 +28,41 @@ public TokenResponseDto login(UserLoginRequestDto req) { if (!encoder.matches(req.password(), user.getPassword())) { throw new ApplicationException(UserErrorCase.INVALID_CREDENTIALS); } - String token = jwtProvider.generateToken(user.getId(), user.getUserType().name()); - return new TokenResponseDto(token); + String accessToken = jwtProvider.generateToken(user.getId(), user.getUserType().name()); + String refreshToken = jwtProvider.generateRefreshToken(user.getId()); + Long expiry = jwtProvider.getTokenExpiry(refreshToken); + + refreshTokenRepository.deleteByUserId(user.getId()); + + RefreshToken entity = RefreshToken.builder() + .userId(user.getId()) + .token(refreshToken) + .expiry(expiry) + .build(); + refreshTokenRepository.save(entity); + + return new TokenResponseDto(accessToken, refreshToken); + } + + public TokenResponseDto reissue(String refreshToken) { + if (!jwtProvider.validateToken(refreshToken)) { + throw new ApplicationException(UserErrorCase.INVALID_CREDENTIALS); + } + Long userId = jwtProvider.getUserId(refreshToken); + RefreshToken saved = refreshTokenRepository.findByUserId(userId) + .orElseThrow(() -> new ApplicationException(UserErrorCase.INVALID_CREDENTIALS)); + + if (!saved.getToken().equals(refreshToken)) { + throw new ApplicationException(UserErrorCase.INVALID_CREDENTIALS); + } + User user = userRepository.findById(userId) + .orElseThrow(() -> new ApplicationException(UserErrorCase.USER_NOT_FOUND)); + String newAccessToken = jwtProvider.generateToken(userId, user.getUserType().name()); + + return new TokenResponseDto(newAccessToken, refreshToken); + } + + public void logout(Long userId) { + refreshTokenRepository.deleteByUserId(userId); } } From 6e5f2d37ead85401bcad7a36e2085459bf16e6bc Mon Sep 17 00:00:00 2001 From: seungin Date: Fri, 18 Jul 2025 20:17:43 +0900 Subject: [PATCH 5/9] =?UTF-8?q?[refactor]=20UserController=EC=97=90=20Acce?= =?UTF-8?q?ssToken=20=EC=9E=AC=EB=B0=9C=EA=B8=89,=20=EC=9C=A0=EC=A0=80=20?= =?UTF-8?q?=EB=A1=9C=EA=B7=B8=EC=95=84=EC=9B=83=20=EB=A1=9C=EC=A7=81=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 --- .../user/controller/UserController.java | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/src/main/java/com/wayble/server/user/controller/UserController.java b/src/main/java/com/wayble/server/user/controller/UserController.java index 01b7e6db..b9dd747c 100644 --- a/src/main/java/com/wayble/server/user/controller/UserController.java +++ b/src/main/java/com/wayble/server/user/controller/UserController.java @@ -1,5 +1,6 @@ package com.wayble.server.user.controller; +import com.wayble.server.common.config.security.jwt.JwtTokenProvider; import com.wayble.server.common.response.CommonResponse; import com.wayble.server.user.dto.UserLoginRequestDto; import com.wayble.server.user.dto.UserRegisterRequestDto; @@ -7,6 +8,7 @@ import com.wayble.server.user.service.UserService; import com.wayble.server.user.service.auth.AuthService; import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.media.Content; import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.responses.ApiResponse; @@ -24,6 +26,7 @@ public class UserController { private final UserService userService; private final AuthService authService; + private final JwtTokenProvider jwtProvider; @PostMapping("/signup") @Operation( @@ -57,4 +60,48 @@ public CommonResponse login(@RequestBody @Valid UserLoginReque return CommonResponse.success(tokenDto); } + @PostMapping("/reissue") + @Operation( + summary = "AccessToken 재발급", + description = "클라이언트의 accessToken 만료 시, 유효한 refreshToken으로 새로운 accessToken 및 refreshToken을 재발급합니다." + + + ) + @ApiResponses({ + @ApiResponse(responseCode = "200", description = "토큰 재발급 성공", + content = @Content(schema = @Schema(implementation = com.wayble.server.user.dto.token.TokenResponseDto.class))), + @ApiResponse(responseCode = "400", description = "refreshToken이 유효하지 않음", + content = @Content(schema = @Schema(implementation = com.wayble.server.common.response.CommonResponse.class))) + }) + public CommonResponse reissue( + @Parameter(description = "재발급용 refreshToken", required = true) + @RequestParam String refreshToken + ) { + TokenResponseDto tokens = authService.reissue(refreshToken); + return CommonResponse.success(tokens); + } + + @PostMapping("/logout") + @Operation( + summary = "유저 로그아웃", + description = "로그아웃 처리(서버에 저장된 refreshToken을 삭제합니다)." + ) + @ApiResponses({ + @ApiResponse(responseCode = "200", description = "로그아웃 성공"), + @ApiResponse(responseCode = "401", description = "유효하지 않은 accessToken") + }) + public CommonResponse logout( + @Parameter( + description = "사용자 인증용 accessToken (Bearer {token} 형태)", + required = true, + example = "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." + ) + @RequestHeader("Authorization") String accessToken + ) { + String token = accessToken.replace("Bearer ", ""); + Long userId = jwtProvider.getUserId(token); + authService.logout(userId); + return CommonResponse.success(null); + } + } From 4a87a815c446a80f4e58e7d994178bc2b43cc321 Mon Sep 17 00:00:00 2001 From: seungin Date: Fri, 18 Jul 2025 20:51:47 +0900 Subject: [PATCH 6/9] =?UTF-8?q?[refactor]=20Spring=20Security=20=EC=84=A4?= =?UTF-8?q?=EC=A0=95=EC=97=90=20=ED=86=A0=ED=81=B0=20=EC=9E=AC=EB=B0=9C?= =?UTF-8?q?=EA=B8=89,=20=EB=A1=9C=EA=B7=B8=EC=95=84=EC=9B=83=20=ED=97=88?= =?UTF-8?q?=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/wayble/server/common/config/SecurityConfig.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/com/wayble/server/common/config/SecurityConfig.java b/src/main/java/com/wayble/server/common/config/SecurityConfig.java index 9600ebf1..4b4d723b 100644 --- a/src/main/java/com/wayble/server/common/config/SecurityConfig.java +++ b/src/main/java/com/wayble/server/common/config/SecurityConfig.java @@ -36,6 +36,8 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti .requestMatchers( "/api/v1/users/signup", "/api/v1/users/login", + "/api/v1/users/reissue", + "/api/v1/users/logout", "/swagger-ui/**", "/v3/api-docs/**" ).permitAll() From c14d9a43f700211535225e86090c21ca03e6e3e6 Mon Sep 17 00:00:00 2001 From: seungin Date: Fri, 18 Jul 2025 20:55:33 +0900 Subject: [PATCH 7/9] =?UTF-8?q?[fix]=20=EB=A1=9C=EA=B7=B8=EC=95=84?= =?UTF-8?q?=EC=9B=83=EC=8B=9C=20refreshToken=20=EC=82=AD=EC=A0=9C=20?= =?UTF-8?q?=ED=8A=B8=EB=9E=9C=EC=9E=AD=EC=85=98=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/wayble/server/user/service/auth/AuthService.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/com/wayble/server/user/service/auth/AuthService.java b/src/main/java/com/wayble/server/user/service/auth/AuthService.java index dd3d3a9e..a2107530 100644 --- a/src/main/java/com/wayble/server/user/service/auth/AuthService.java +++ b/src/main/java/com/wayble/server/user/service/auth/AuthService.java @@ -9,6 +9,7 @@ import com.wayble.server.user.exception.UserErrorCase; import com.wayble.server.user.repository.RefreshTokenRepository; import com.wayble.server.user.repository.UserRepository; +import jakarta.transaction.Transactional; import lombok.RequiredArgsConstructor; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; @@ -62,6 +63,7 @@ public TokenResponseDto reissue(String refreshToken) { return new TokenResponseDto(newAccessToken, refreshToken); } + @Transactional public void logout(Long userId) { refreshTokenRepository.deleteByUserId(userId); } From f90dc795e983f117727cf225801bcb2ea12403f6 Mon Sep 17 00:00:00 2001 From: seungin Date: Fri, 18 Jul 2025 20:58:45 +0900 Subject: [PATCH 8/9] =?UTF-8?q?[refactor]=20=EB=A1=9C=EA=B7=B8=EC=95=84?= =?UTF-8?q?=EC=9B=83=EC=8B=9C=20=EC=84=B1=EA=B3=B5=20=EB=A9=94=EC=8B=9C?= =?UTF-8?q?=EC=A7=80=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/wayble/server/user/controller/UserController.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/wayble/server/user/controller/UserController.java b/src/main/java/com/wayble/server/user/controller/UserController.java index b9dd747c..c95fb080 100644 --- a/src/main/java/com/wayble/server/user/controller/UserController.java +++ b/src/main/java/com/wayble/server/user/controller/UserController.java @@ -90,7 +90,7 @@ public CommonResponse reissue( @ApiResponse(responseCode = "200", description = "로그아웃 성공"), @ApiResponse(responseCode = "401", description = "유효하지 않은 accessToken") }) - public CommonResponse logout( + public CommonResponse logout( @Parameter( description = "사용자 인증용 accessToken (Bearer {token} 형태)", required = true, @@ -101,7 +101,7 @@ public CommonResponse logout( String token = accessToken.replace("Bearer ", ""); Long userId = jwtProvider.getUserId(token); authService.logout(userId); - return CommonResponse.success(null); + return CommonResponse.success("로그아웃에 성공하였습니다."); } } From 7add5455b7d3a9d297004eedff4c77a872a7efd5 Mon Sep 17 00:00:00 2001 From: seungin Date: Fri, 18 Jul 2025 22:00:43 +0900 Subject: [PATCH 9/9] =?UTF-8?q?[refactor]=20=EC=BD=94=EB=93=9C=EB=A6=AC?= =?UTF-8?q?=EB=B7=B0=20=EB=B0=98=EC=98=81=ED=95=98=EC=97=AC=20=EB=A6=AC?= =?UTF-8?q?=ED=8C=A9=ED=86=A0=EB=A7=81=20=EC=A7=84=ED=96=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../config/security/jwt/JwtTokenProvider.java | 3 +++ .../server/user/controller/UserController.java | 8 ++++++++ .../wayble/server/user/entity/RefreshToken.java | 8 ++++++++ .../user/repository/RefreshTokenRepository.java | 3 +++ .../server/user/service/auth/AuthService.java | 17 +++++++++++++++-- 5 files changed, 37 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/wayble/server/common/config/security/jwt/JwtTokenProvider.java b/src/main/java/com/wayble/server/common/config/security/jwt/JwtTokenProvider.java index 018704e6..033cc9ff 100644 --- a/src/main/java/com/wayble/server/common/config/security/jwt/JwtTokenProvider.java +++ b/src/main/java/com/wayble/server/common/config/security/jwt/JwtTokenProvider.java @@ -64,6 +64,9 @@ public Long getUserId(String token) { } public Long getTokenExpiry(String token) { + if (!validateToken(token)) { + throw new IllegalArgumentException("Invalid token"); + } return Jwts.parserBuilder().setSigningKey(signingKey).build() .parseClaimsJws(token).getBody().getExpiration().getTime(); } diff --git a/src/main/java/com/wayble/server/user/controller/UserController.java b/src/main/java/com/wayble/server/user/controller/UserController.java index c95fb080..1d650fa5 100644 --- a/src/main/java/com/wayble/server/user/controller/UserController.java +++ b/src/main/java/com/wayble/server/user/controller/UserController.java @@ -1,10 +1,12 @@ package com.wayble.server.user.controller; import com.wayble.server.common.config.security.jwt.JwtTokenProvider; +import com.wayble.server.common.exception.ApplicationException; import com.wayble.server.common.response.CommonResponse; import com.wayble.server.user.dto.UserLoginRequestDto; import com.wayble.server.user.dto.UserRegisterRequestDto; import com.wayble.server.user.dto.token.TokenResponseDto; +import com.wayble.server.user.exception.UserErrorCase; import com.wayble.server.user.service.UserService; import com.wayble.server.user.service.auth.AuthService; import io.swagger.v3.oas.annotations.Operation; @@ -98,7 +100,13 @@ public CommonResponse logout( ) @RequestHeader("Authorization") String accessToken ) { + if (accessToken == null || !accessToken.startsWith("Bearer ")) { + throw new ApplicationException(UserErrorCase.INVALID_CREDENTIALS); + } String token = accessToken.replace("Bearer ", ""); + if (token.isEmpty()) { + throw new ApplicationException(UserErrorCase.INVALID_CREDENTIALS); + } Long userId = jwtProvider.getUserId(token); authService.logout(userId); return CommonResponse.success("로그아웃에 성공하였습니다."); diff --git a/src/main/java/com/wayble/server/user/entity/RefreshToken.java b/src/main/java/com/wayble/server/user/entity/RefreshToken.java index 7036eb76..5fdecb6e 100644 --- a/src/main/java/com/wayble/server/user/entity/RefreshToken.java +++ b/src/main/java/com/wayble/server/user/entity/RefreshToken.java @@ -23,4 +23,12 @@ public class RefreshToken { @Column(nullable = false) private Long expiry; // 만료 시간 + + public void setToken(String token) { + this.token = token; + } + + public void setExpiry(Long expiry) { + this.expiry = expiry; + } } \ No newline at end of file diff --git a/src/main/java/com/wayble/server/user/repository/RefreshTokenRepository.java b/src/main/java/com/wayble/server/user/repository/RefreshTokenRepository.java index eefa8bd2..f23e2252 100644 --- a/src/main/java/com/wayble/server/user/repository/RefreshTokenRepository.java +++ b/src/main/java/com/wayble/server/user/repository/RefreshTokenRepository.java @@ -1,6 +1,7 @@ package com.wayble.server.user.repository; import com.wayble.server.user.entity.RefreshToken; +import jakarta.transaction.Transactional; import org.springframework.data.jpa.repository.JpaRepository; import java.util.Optional; @@ -8,5 +9,7 @@ public interface RefreshTokenRepository extends JpaRepository { Optional findByUserId(Long userId); Optional findByToken(String token); + + @Transactional void deleteByUserId(Long userId); } \ No newline at end of file diff --git a/src/main/java/com/wayble/server/user/service/auth/AuthService.java b/src/main/java/com/wayble/server/user/service/auth/AuthService.java index a2107530..e73f338e 100644 --- a/src/main/java/com/wayble/server/user/service/auth/AuthService.java +++ b/src/main/java/com/wayble/server/user/service/auth/AuthService.java @@ -56,11 +56,24 @@ public TokenResponseDto reissue(String refreshToken) { if (!saved.getToken().equals(refreshToken)) { throw new ApplicationException(UserErrorCase.INVALID_CREDENTIALS); } + + if (saved.getExpiry() < System.currentTimeMillis()) { + refreshTokenRepository.delete(saved); + throw new ApplicationException(UserErrorCase.INVALID_CREDENTIALS); + } + User user = userRepository.findById(userId) .orElseThrow(() -> new ApplicationException(UserErrorCase.USER_NOT_FOUND)); - String newAccessToken = jwtProvider.generateToken(userId, user.getUserType().name()); - return new TokenResponseDto(newAccessToken, refreshToken); + String newRefreshToken = jwtProvider.generateRefreshToken(userId); + Long newExpiry = jwtProvider.getTokenExpiry(newRefreshToken); + + saved.setToken(newRefreshToken); + saved.setExpiry(newExpiry); + refreshTokenRepository.save(saved); + + String newAccessToken = jwtProvider.generateToken(userId, user.getUserType().name()); + return new TokenResponseDto(newAccessToken, newRefreshToken); } @Transactional