[공부정리] ExpiredJwtException를 이용한 토큰 재발급

jeyong·2024년 3월 9일
0

공부 / 생각 정리  

목록 보기
41/120
post-custom-banner

이번에 작성할 주제는 만료된 JWT을 재발급하는 방법에 대해서 간단하게 작성해보겠다.

1. Access Token과 Refresh Token

JWT 토큰 시스템에서는 일반적으로 Access Token과 Refresh Token이 사용된다. Access Token은 짧은 유효 시간을 가지며, 만료 시 Refresh Token을 통해 재발급 받는 방식이다. 이 과정에서 발생할 수 있는 문제점과 해결 방안을 프로젝트 코드를 통해 설명하겠다.

1-1. Access Token과 Refresh Token 페이로드가 동일

@Transactional(readOnly = true)
    public SignInResponse signIn(SignInRequest req) {
        Member member = memberRepository.findWithRolesByEmail(req.getEmail()).orElseThrow(LoginFailureException::new);
        validatePassword(req, member);
        TokenHandler.PrivateClaims privateClaims = createPrivateClaims(member);
        String accessToken = userAccessTokenHandler.createToken(privateClaims);
        String refreshToken = userRefreshTokenHandler.createToken(privateClaims);
        return new SignInResponse(accessToken, refreshToken);
    }

Payload에 중요 정보를 담지 않기 때문에, Access Token과 Refresh Token의 페이로드를 동일하게 생성할 수 있다.

 public UserRefreshTokenResponse refreshToken(String accountRefreshToken) {
        TokenHandler.PrivateClaims accountClaims = userRefreshTokenHandler.parse(accountRefreshToken).orElseThrow(RefreshTokenFailureException::new);
        String accountAccessToken = userAccessTokenHandler.createToken(accountClaims);
        return new UserRefreshTokenResponse(accountAccessToken);
    }

Payload를 동일한 내용으로 생성하기 때문에 Refresh Token을 이용하여 Access Token을 효율적으로 재생성할 수 있게 한다.

1-2. Access Token과 Refresh Token 페이로드가 다름

public StoreSelectResponse storeSelect(StoreSelectRequest req) {
        Store store = storeRepository.findByIdAndMemberId(req.getStoreId(), req.getMemberId())
                .orElseThrow(SelectStoreFailureException::new);
        TokenHandler.PrivateClaims privateClaims = createPrivateClaims(store);
        String storeAccessToken = storeAccessTokenHandler.createToken(privateClaims);
        return new StoreSelectResponse(storeAccessToken);
    }

하지만 어쩔 수 없이 다른 Payload를 저장해야할 때가 있다. 사용자가 본인의 상점 중 하나의 상점을 선택하면 상점의 Access Token을 생성해주어야한다. 그래서 상점의 Access Token에는 상점에 대한 정보가 담겨있다. 그래서 상점 Access Token의 Refresh Token인 사용자의 Access Token과는 다른 Payload가 담기게 된다.

public StoreRefreshTokenResponse refreshStoreToken(StoreRefreshTokenRequest req) {
        String expiredStoreToken = req.getExpiredStoreToken();
        try {
            TokenHandler.PrivateClaims privateClaims = storeAccessTokenHandler.parse(expiredStoreToken).orElseThrow(RefreshTokenFailureException::new);
            String newStoreAccessToken = storeAccessTokenHandler.createToken(privateClaims);
            return new StoreRefreshTokenResponse(newStoreAccessToken);
        } catch (ExpiredJwtException e) {
            Claims claims = e.getClaims();
            TokenHandler.PrivateClaims privateClaims = storeAccessTokenHandler.convert(claims);
            String newStoreAccessToken = storeAccessTokenHandler.createToken(privateClaims);
            return new StoreRefreshTokenResponse(newStoreAccessToken);
        }
    }

ExpiredJwtException을 이용해서 처리해주면 된다. 상점의 Access Token에서 Payload를 추출할 때 ExpiredJwtException가 발생하면 ExpiredJwtException에 담겨있는 Payload를 이용하면 된다.

2. ExpiredJwtException

public class ExpiredJwtException extends ClaimJwtException {

    public ExpiredJwtException(Header header, Claims claims, String message) {
        super(header, claims, message);
    }

    /**
     * @param header jwt header
     * @param claims jwt claims (body)
     * @param message exception message
     * @param cause cause
     * @since 0.5
     */
    public ExpiredJwtException(Header header, Claims claims, String message, Throwable cause) {
        super(header, claims, message, cause);
    }
}

ExpiredJwtException은 JWT 토큰이 만료되었을 때 발생하는 예외로, 헤더(header), 클레임(claims), 메시지(message) 등의 정보를 담고 있다. 이 예외를 활용하면 만료된 토큰의 정보를 안전하게 추출하고, 필요한 처리를 수행할 수 있다.
참고로 다른 Key로 생성한 Token에서 ExpiredJwtException을 발생시키는 경우는없다. 다른 Key로 생성한 Token은 SignatureException을 발생시킨다.

profile
노를 젓다 보면 언젠가는 물이 들어오겠지.
post-custom-banner

0개의 댓글