diff --git a/services/auth/src/main/java/com/ticketPing/auth/application/service/AuthService.java b/services/auth/src/main/java/com/ticketPing/auth/application/service/AuthService.java index 55844a3f..bde25986 100644 --- a/services/auth/src/main/java/com/ticketPing/auth/application/service/AuthService.java +++ b/services/auth/src/main/java/com/ticketPing/auth/application/service/AuthService.java @@ -5,12 +5,9 @@ import com.ticketPing.auth.application.dto.TokenResponse; import com.ticketPing.auth.common.enums.Role; import com.ticketPing.auth.common.exception.AuthErrorCase; -import com.ticketPing.auth.infrastructure.jwt.JwtTokenProvider; import com.ticketPing.auth.presentation.request.LoginRequest; import exception.ApplicationException; import io.jsonwebtoken.Claims; -import io.jsonwebtoken.ExpiredJwtException; -import io.jsonwebtoken.JwtException; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import lombok.RequiredArgsConstructor; @@ -26,39 +23,43 @@ @Service @RequiredArgsConstructor public class AuthService { - private final JwtTokenProvider jwtTokenProvider; - private final RefreshTokenCacheService refreshTokenCacheService; - private final RefreshTokenCookieService refreshTokenCookieService; + private final TokenService tokenService; + private final CacheService cacheService; + private final CookieService cookieService; private final UserClient userClient; public TokenResponse login(LoginRequest loginRequest, HttpServletResponse response) { UUID userId = authenticateUser(loginRequest); - createAndSaveRefreshToken(userId, response); + createAndSaveRefreshToken(userId, Role.USER, response); String accessToken = createAccessToken(userId, Role.USER); return TokenResponse.of(accessToken); } public UserCacheDto validateToken(String authHeader) { - String accessToken = jwtTokenProvider.parseToken(authHeader); - jwtTokenProvider.validateToken(accessToken); - Claims claims = jwtTokenProvider.getClaimsFromToken(accessToken); + String accessToken = tokenService.parseToken(authHeader); + tokenService.validateToken(accessToken); + Claims claims = tokenService.getClaimsFromToken(accessToken); return extractUserFromClaims(claims); } - public TokenResponse refreshAccessToken(String authHeader, HttpServletRequest request, HttpServletResponse response) { - Claims claims = validateAndExtractClaimsFromExpiredToken(authHeader, response); - UUID userId = jwtTokenProvider.getUserId(claims); - Role userRole = jwtTokenProvider.getUserRole(claims); + public TokenResponse refreshAccessToken(HttpServletRequest request, HttpServletResponse response) { + String cookieRefreshToken = getValidatedRefreshToken(request); - validateAndRotateRefreshToken(userId, request, response); + Claims claims = tokenService.getClaimsFromToken(cookieRefreshToken); + UUID userId = tokenService.getUserId(claims); + Role userRole = tokenService.getUserRole(claims); + validateStoredRefreshToken(cookieRefreshToken, userId, response); + + createAndSaveRefreshToken(userId, userRole, response); String newAccessToken = createAccessToken(userId, userRole); + return TokenResponse.of(newAccessToken); } public void logout(UUID userId, HttpServletResponse response) { - refreshTokenCacheService.deleteRefreshToken(userId); - refreshTokenCookieService.deleteRefreshToken(response); + cacheService.deleteRefreshToken(userId); + cookieService.deleteRefreshToken(response); } private UUID authenticateUser(LoginRequest loginRequest) { @@ -68,47 +69,32 @@ private UUID authenticateUser(LoginRequest loginRequest) { } private String createAccessToken(UUID userId, Role role) { - return BEARER_PREFIX + jwtTokenProvider.createAccessToken(userId, role); + return BEARER_PREFIX + tokenService.createAccessToken(userId, role); } - private void createAndSaveRefreshToken(UUID userId, HttpServletResponse response) { - String refreshToken = jwtTokenProvider.createRefreshToken(userId); - refreshTokenCacheService.saveRefreshToken(userId, refreshToken); - refreshTokenCookieService.setRefreshToken(response, refreshToken); + private void createAndSaveRefreshToken(UUID userId, Role role, HttpServletResponse response) { + String refreshToken = tokenService.createRefreshToken(userId, role); + cacheService.saveRefreshToken(userId, refreshToken); + cookieService.setRefreshToken(response, refreshToken); } - private Claims validateAndExtractClaimsFromExpiredToken(String authHeader, HttpServletResponse response) { - String token = jwtTokenProvider.parseToken(authHeader); - try { - Claims claims = jwtTokenProvider.getClaimsFromToken(token); - UUID userId = jwtTokenProvider.getUserId(claims); - logout(userId, response); - throw new ApplicationException(AuthErrorCase.ACCESS_TOKEN_NOT_EXPIRED); - } catch (ExpiredJwtException e) { - return e.getClaims(); - } catch (JwtException e) { - throw new ApplicationException(AuthErrorCase.INVALID_TOKEN); - } + private UserCacheDto extractUserFromClaims(Claims claims) { + UUID userId = tokenService.getUserId(claims); + Role role = tokenService.getUserRole(claims); + return new UserCacheDto(userId, role.getValue()); } - private void validateAndRotateRefreshToken(UUID userId, HttpServletRequest request, HttpServletResponse response) { - String storedRefreshToken = refreshTokenCacheService.getRefreshToken(userId); - String cookieRefreshToken = refreshTokenCookieService.getRefreshToken(request); - validateRefreshToken(userId, response, cookieRefreshToken, storedRefreshToken); - createAndSaveRefreshToken(userId, response); + private String getValidatedRefreshToken(HttpServletRequest request) { + String refreshToken = cookieService.getRefreshToken(request); + tokenService.validateToken(refreshToken); + return refreshToken; } - private void validateRefreshToken(UUID userId, HttpServletResponse response, String cookieRefreshToken, String storedRefreshToken) { + private void validateStoredRefreshToken(String cookieRefreshToken, UUID userId, HttpServletResponse response) { + String storedRefreshToken = cacheService.getRefreshToken(userId); if (!cookieRefreshToken.equals(storedRefreshToken)) { logout(userId, response); throw new ApplicationException(AuthErrorCase.INVALID_REFRESH_TOKEN); } - jwtTokenProvider.validateToken(storedRefreshToken); - } - - private UserCacheDto extractUserFromClaims(Claims claims) { - UUID userId = jwtTokenProvider.getUserId(claims); - Role role = jwtTokenProvider.getUserRole(claims); - return new UserCacheDto(userId, role.getValue()); } } \ No newline at end of file diff --git a/services/auth/src/main/java/com/ticketPing/auth/application/service/CacheService.java b/services/auth/src/main/java/com/ticketPing/auth/application/service/CacheService.java new file mode 100644 index 00000000..6a496f6e --- /dev/null +++ b/services/auth/src/main/java/com/ticketPing/auth/application/service/CacheService.java @@ -0,0 +1,11 @@ +package com.ticketPing.auth.application.service; + +import java.util.UUID; + +public interface CacheService { + public void saveRefreshToken(UUID userId, String refreshToken); + + public String getRefreshToken(UUID userId); + + public void deleteRefreshToken(UUID userId); +} diff --git a/services/auth/src/main/java/com/ticketPing/auth/application/service/CookieService.java b/services/auth/src/main/java/com/ticketPing/auth/application/service/CookieService.java new file mode 100644 index 00000000..4cba0762 --- /dev/null +++ b/services/auth/src/main/java/com/ticketPing/auth/application/service/CookieService.java @@ -0,0 +1,12 @@ +package com.ticketPing.auth.application.service; + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + +public interface CookieService { + public void setRefreshToken(HttpServletResponse response, String refreshToken); + + public String getRefreshToken(HttpServletRequest request); + + public void deleteRefreshToken(HttpServletResponse response); +} diff --git a/services/auth/src/main/java/com/ticketPing/auth/application/service/TokenService.java b/services/auth/src/main/java/com/ticketPing/auth/application/service/TokenService.java new file mode 100644 index 00000000..07daa26d --- /dev/null +++ b/services/auth/src/main/java/com/ticketPing/auth/application/service/TokenService.java @@ -0,0 +1,22 @@ +package com.ticketPing.auth.application.service; + +import com.ticketPing.auth.common.enums.Role; +import io.jsonwebtoken.Claims; +import java.util.UUID; + +public interface TokenService { + + public String createAccessToken(UUID userId, Role role); + + public String createRefreshToken(UUID userId, Role role); + + public String parseToken(String authHeader); + + public void validateToken(String token); + + public Claims getClaimsFromToken(String token); + + public UUID getUserId(Claims claims); + + public Role getUserRole(Claims claims); +} diff --git a/services/auth/src/main/java/com/ticketPing/auth/common/constants/AuthConstants.java b/services/auth/src/main/java/com/ticketPing/auth/common/constants/AuthConstants.java index ffd95541..39bcb7d0 100644 --- a/services/auth/src/main/java/com/ticketPing/auth/common/constants/AuthConstants.java +++ b/services/auth/src/main/java/com/ticketPing/auth/common/constants/AuthConstants.java @@ -1,9 +1,20 @@ package com.ticketPing.auth.common.constants; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +@Component public final class AuthConstants { public static final String AUTHORIZATION_HEADER = "Authorization"; public static final String BEARER_PREFIX = "Bearer "; public static final String REFRESH_COOKIE = "refreshToken"; - private AuthConstants() {} + public static long REFRESH_TOKEN_EXPIRATION; + public static long ACCESS_TOKEN_EXPIRATION; + + private AuthConstants(@Value("${jwt.refreshToken.expiration}") long refreshTokenExpiration, + @Value("${jwt.accessToken.expiration}") long accessTokenExpiration) { + REFRESH_TOKEN_EXPIRATION = refreshTokenExpiration; + ACCESS_TOKEN_EXPIRATION = accessTokenExpiration; + } } \ No newline at end of file diff --git a/services/auth/src/main/java/com/ticketPing/auth/application/service/RefreshTokenCookieService.java b/services/auth/src/main/java/com/ticketPing/auth/infrastructure/service/HttpCookieService.java similarity index 76% rename from services/auth/src/main/java/com/ticketPing/auth/application/service/RefreshTokenCookieService.java rename to services/auth/src/main/java/com/ticketPing/auth/infrastructure/service/HttpCookieService.java index e245bf18..8cc9a99b 100644 --- a/services/auth/src/main/java/com/ticketPing/auth/application/service/RefreshTokenCookieService.java +++ b/services/auth/src/main/java/com/ticketPing/auth/infrastructure/service/HttpCookieService.java @@ -1,28 +1,27 @@ -package com.ticketPing.auth.application.service; +package com.ticketPing.auth.infrastructure.service; +import com.ticketPing.auth.application.service.CookieService; import com.ticketPing.auth.common.exception.AuthErrorCase; import com.ticketPing.auth.infrastructure.http.HttpCookieManager; import exception.ApplicationException; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import lombok.RequiredArgsConstructor; -import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import java.util.concurrent.TimeUnit; import static com.ticketPing.auth.common.constants.AuthConstants.REFRESH_COOKIE; +import static com.ticketPing.auth.common.constants.AuthConstants.REFRESH_TOKEN_EXPIRATION; @Service @RequiredArgsConstructor -public class RefreshTokenCookieService { +public class HttpCookieService implements CookieService { - @Value("${jwt.refreshToken.expiration}") - private long refreshTokenExpiration; private final HttpCookieManager cookieManager; public void setRefreshToken(HttpServletResponse response, String refreshToken) { - cookieManager.setCookie(response, REFRESH_COOKIE, refreshToken, (int) TimeUnit.MILLISECONDS.toSeconds(refreshTokenExpiration)); + cookieManager.setCookie(response, REFRESH_COOKIE, refreshToken, (int) TimeUnit.MILLISECONDS.toSeconds(REFRESH_TOKEN_EXPIRATION)); } public String getRefreshToken(HttpServletRequest request) { diff --git a/services/auth/src/main/java/com/ticketPing/auth/infrastructure/jwt/JwtTokenProvider.java b/services/auth/src/main/java/com/ticketPing/auth/infrastructure/service/JwtTokenService.java similarity index 80% rename from services/auth/src/main/java/com/ticketPing/auth/infrastructure/jwt/JwtTokenProvider.java rename to services/auth/src/main/java/com/ticketPing/auth/infrastructure/service/JwtTokenService.java index c4dd56b5..25c13195 100644 --- a/services/auth/src/main/java/com/ticketPing/auth/infrastructure/jwt/JwtTokenProvider.java +++ b/services/auth/src/main/java/com/ticketPing/auth/infrastructure/service/JwtTokenService.java @@ -1,5 +1,6 @@ -package com.ticketPing.auth.infrastructure.jwt; +package com.ticketPing.auth.infrastructure.service; +import com.ticketPing.auth.application.service.TokenService; import com.ticketPing.auth.common.exception.AuthErrorCase; import com.ticketPing.auth.common.enums.Role; import exception.ApplicationException; @@ -13,22 +14,16 @@ import java.util.Date; import java.util.UUID; -import static com.ticketPing.auth.common.constants.AuthConstants.BEARER_PREFIX; +import static com.ticketPing.auth.common.constants.AuthConstants.*; @Component -public class JwtTokenProvider { +public class JwtTokenService implements TokenService { private final Key secretKey; - private final long accessTokenExpiration; - private final long refreshTokenExpiration; - public JwtTokenProvider(@Value("${jwt.secret}") String secret, - @Value("${jwt.accessToken.expiration}") long accessTokenExpiration, - @Value("${jwt.refreshToken.expiration}") long refreshTokenExpiration) { + public JwtTokenService(@Value("${jwt.secret}") String secret) { byte[] bytes = Base64.getDecoder().decode(secret); this.secretKey = Keys.hmacShaKeyFor(bytes); - this.accessTokenExpiration = accessTokenExpiration; - this.refreshTokenExpiration = refreshTokenExpiration; } public String createAccessToken(UUID userId, Role role) { @@ -37,17 +32,18 @@ public String createAccessToken(UUID userId, Role role) { .setSubject(userId.toString()) .claim("role", role) .setIssuedAt(now) - .setExpiration(new Date(now.getTime() + accessTokenExpiration)) + .setExpiration(new Date(now.getTime() + ACCESS_TOKEN_EXPIRATION)) .signWith(this.secretKey, SignatureAlgorithm.HS256) .compact(); } - public String createRefreshToken(UUID userId) { + public String createRefreshToken(UUID userId, Role role) { Date now = new Date(); return Jwts.builder() .setSubject(userId.toString()) + .claim("role", role) .setIssuedAt(now) - .setExpiration(new Date(now.getTime() + refreshTokenExpiration)) + .setExpiration(new Date(now.getTime() + REFRESH_TOKEN_EXPIRATION)) .signWith(this.secretKey, SignatureAlgorithm.HS256) .compact(); } diff --git a/services/auth/src/main/java/com/ticketPing/auth/application/service/RefreshTokenCacheService.java b/services/auth/src/main/java/com/ticketPing/auth/infrastructure/service/RedisCacheService.java similarity index 71% rename from services/auth/src/main/java/com/ticketPing/auth/application/service/RefreshTokenCacheService.java rename to services/auth/src/main/java/com/ticketPing/auth/infrastructure/service/RedisCacheService.java index b9ef2d1d..4a85e1bb 100644 --- a/services/auth/src/main/java/com/ticketPing/auth/application/service/RefreshTokenCacheService.java +++ b/services/auth/src/main/java/com/ticketPing/auth/infrastructure/service/RedisCacheService.java @@ -1,27 +1,28 @@ -package com.ticketPing.auth.application.service; +package com.ticketPing.auth.infrastructure.service; import caching.repository.RedisRepository; +import com.ticketPing.auth.application.service.CacheService; import com.ticketPing.auth.common.exception.AuthErrorCase; import exception.ApplicationException; import lombok.RequiredArgsConstructor; -import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import java.time.Duration; import java.util.Optional; import java.util.UUID; +import static com.ticketPing.auth.common.constants.AuthConstants.REFRESH_TOKEN_EXPIRATION; + @Service @RequiredArgsConstructor -public class RefreshTokenCacheService { +public class RedisCacheService implements CacheService { - @Value("${jwt.refreshToken.expiration}") - private long refreshTokenExpiration; private final RedisRepository redisRepository; public void saveRefreshToken(UUID userId, String refreshToken) { String key = generateKey(userId); - redisRepository.setValueWithTTL(key, refreshToken, Duration.ofMillis(refreshTokenExpiration)); + System.out.println(REFRESH_TOKEN_EXPIRATION); + redisRepository.setValueWithTTL(key, refreshToken, Duration.ofMillis(REFRESH_TOKEN_EXPIRATION)); } public String getRefreshToken(UUID userId) { @@ -36,7 +37,7 @@ public void deleteRefreshToken(UUID userId) { } private String generateKey(UUID userId) { - return String.format("auth:refreshToken:user:%s", userId); + return String.format("refreshToken:%s", userId); } } diff --git a/services/auth/src/main/java/com/ticketPing/auth/presentation/controller/AuthController.java b/services/auth/src/main/java/com/ticketPing/auth/presentation/controller/AuthController.java index 808c281a..21f2fd0a 100644 --- a/services/auth/src/main/java/com/ticketPing/auth/presentation/controller/AuthController.java +++ b/services/auth/src/main/java/com/ticketPing/auth/presentation/controller/AuthController.java @@ -47,9 +47,8 @@ public ResponseEntity> validateToken(@RequestHeader @Operation(summary = "토큰 재발급") @PostMapping("/refresh") - public ResponseEntity> refreshAccessToken(@RequestHeader(AUTHORIZATION_HEADER) String authHeader, - HttpServletRequest request, HttpServletResponse response) { - TokenResponse tokenResponse = authService.refreshAccessToken(authHeader, request, response); + public ResponseEntity> refreshAccessToken(HttpServletRequest request, HttpServletResponse response) { + TokenResponse tokenResponse = authService.refreshAccessToken(request, response); return ResponseEntity .status(HttpStatus.OK) .body(CommonResponse.success(tokenResponse));