Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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) {
Expand All @@ -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());
}
}
Original file line number Diff line number Diff line change
@@ -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);
}
Original file line number Diff line number Diff line change
@@ -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);
}
Original file line number Diff line number Diff line change
@@ -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);
}
Original file line number Diff line number Diff line change
@@ -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;
}
}
Original file line number Diff line number Diff line change
@@ -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) {
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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) {
Expand All @@ -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();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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) {
Expand All @@ -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);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,8 @@ public ResponseEntity<CommonResponse<UserCacheDto>> validateToken(@RequestHeader

@Operation(summary = "토큰 재발급")
@PostMapping("/refresh")
public ResponseEntity<CommonResponse<TokenResponse>> refreshAccessToken(@RequestHeader(AUTHORIZATION_HEADER) String authHeader,
HttpServletRequest request, HttpServletResponse response) {
TokenResponse tokenResponse = authService.refreshAccessToken(authHeader, request, response);
public ResponseEntity<CommonResponse<TokenResponse>> refreshAccessToken(HttpServletRequest request, HttpServletResponse response) {
TokenResponse tokenResponse = authService.refreshAccessToken(request, response);
return ResponseEntity
.status(HttpStatus.OK)
.body(CommonResponse.success(tokenResponse));
Expand Down
Loading