728x90

Spring Boot에서 Redis를 이용한 Refresh Token 처리 방법은 다음과 같습니다.

  1. 의존성 추가
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
  1. Redis 설정
spring.redis.host=localhost
spring.redis.port=6379
spring.redis.password=
spring.redis.database=0
spring.redis.jedis.pool.max-active=8
spring.redis.jedis.pool.max-wait=-1
spring.redis.jedis.pool.max-idle=8
spring.redis.jedis.pool.min-idle=0
  1. Refresh Token 생성 후 Redis에 저장
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import java.time.Duration;
import java.util.UUID;

@Component
public class RefreshTokenService {

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    private final String PREFIX_REFRESH_TOKEN = "refresh_token:";

    // Refresh Token 만료시간 (1주일)
    private final Duration REFRESH_TOKEN_EXPIRATION = Duration.ofDays(7);

    public String createRefreshToken() {
        String refreshToken = PREFIX_REFRESH_TOKEN + UUID.randomUUID().toString();
        redisTemplate.opsForValue().set(refreshToken, "", REFRESH_TOKEN_EXPIRATION);
        return refreshToken;
    }
}
  1. 1. Access Token 만료 시, Refresh Token을 이용하여 새로운 Access Token을 생성.
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import java.time.Duration;
import java.util.Date;

@Component
public class AccessTokenService {

    @Value("${jwt.secret}")
    private String jwtSecret;

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    private final String PREFIX_REFRESH_TOKEN = "refresh_token:";

    // Access Token 만료시간 (1시간)
    private final Duration ACCESS_TOKEN_EXPIRATION = Duration.ofHours(1);

    public String createAccessToken(String refreshToken) throws Exception {
        if (!redisTemplate.hasKey(refreshToken)) {
            throw new Exception("Invalid Refresh Token");
        }

        String subject = ""; // Access Token의 subject
        Date now = new Date();
        Date expiredAt = new Date(now.getTime() + ACCESS_TOKEN_EXPIRATION.toMillis());

        redisTemplate.expire(refreshToken, Duration.ZERO);

        String accessToken = Jwts.builder()
                .setSubject(subject)
                .setIssuedAt(now)
                .setExpiration(expiredAt)
                .signWith(SignatureAlgorithm.HS256, jwtSecret)
                .compact();

        return accessToken;
    }
}

예시 코드는 Redis에 Refresh Token만 저장하고, Access Token은 JWT로 생성합니다.

이외에도 Redis를 이용하여 Access Token까지 저장할 수 있습니다.

  1. Redis에 Access Token과 Refresh Token을 저장합니다.
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import java.time.Duration;
import java.util.UUID;

@Component
public class TokenService {

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    private final String PREFIX_ACCESS_TOKEN = "access_token:";
    private final String PREFIX_REFRESH_TOKEN = "refresh_token:";

    // Access Token 만료시간 (1시간)
    private final Duration ACCESS_TOKEN_EXPIRATION = Duration.ofHours(1);

    // Refresh Token 만료시간 (1주일)
    private final Duration REFRESH_TOKEN_EXPIRATION = Duration.ofDays(7);

    public String createAccessToken() {
        String accessToken = PREFIX_ACCESS_TOKEN + UUID.randomUUID().toString();
        redisTemplate.opsForValue().set(accessToken, "", ACCESS_TOKEN_EXPIRATION);
        return accessToken;
    }

    public String createRefreshToken() {
        String refreshToken = PREFIX_REFRESH_TOKEN + UUID.randomUUID().toString();
        redisTemplate.opsForValue().set(refreshToken, "", REFRESH_TOKEN_EXPIRATION);
        return refreshToken;
    }

    public void deleteToken(String accessToken) {
        String refreshToken = accessTokenToRefreshToken(accessToken);
        redisTemplate.delete(accessToken, refreshToken);
    }

    public String accessTokenToRefreshToken(String accessToken) {
        return accessToken.replace(PREFIX_ACCESS_TOKEN, PREFIX_REFRESH_TOKEN);
    }

    public boolean isValidRefreshToken(String refreshToken) {
        return redisTemplate.hasKey(refreshToken);
    }

    public boolean isValidAccessToken(String accessToken) {
        return redisTemplate.hasKey(accessToken);
    }
}
  1. Access Token의 만료시간을 Redis에서 확인합니다.
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.util.Date;

@Component
public class JwtService {

    @Value("${jwt.secret}")
    private String jwtSecret;

    @Autowired
    private TokenService tokenService;

    // Access Token 만료시간 (1시간)
    private final long ACCESS_TOKEN_EXPIRATION_TIME = 3600000L;

    public String createAccessToken() {
        String accessToken = tokenService.createAccessToken();

        String subject = ""; // Access Token의 subject
        Date now = new Date();
        Date expiredAt = new Date(now.getTime() + ACCESS_TOKEN_EXPIRATION_TIME);

        String refreshToken = tokenService.createRefreshToken();

        // Redis에 Access Token과 Refresh Token 저장
        RedisTemplate<String, String> redisTemplate = tokenService.getRedisTemplate();
        redisTemplate.opsForValue().set(accessToken, refreshToken);
        redisTemplate.expire(accessToken, ACCESS_TOKEN_EXPIRATION_TIME);
        redisTemplate.opsForValue().set(refreshToken, accessToken);
        redisTemplate.expire(refreshToken, tokenService.getREFRESH_TOKEN_EXPIRATION_TIME());

        String jwt = Jwts.builder()
                .setSubject(subject)
                .setIssuedAt(now)
                .setExpiration(expiredAt)
                .signWith(SignatureAlgorithm.HS256, jwtSecret)
                .compact();

        return jwt;
    }

    public String refreshToken(String refreshToken) throws Exception {
        if (!tokenService.isValidRefreshToken(refreshToken)) {
            throw new Exception("Invalid Refresh Token");
        }

        String accessToken = tokenService.accessTokenToRefreshToken(refreshToken);
        if (!tokenService.isValidAccessToken(accessToken)) {
            throw new Exception("Invalid Access Token");
        }

        // Access Token의 만료시간을 Redis에서 확인
        RedisTemplate<String, String> redisTemplate = tokenService.getRedisTemplate();
        long expiration = redisTemplate.getExpire(accessToken);

        if (expiration < 0) {
            throw new Exception("Expired Access Token");
        }

        String newAccessToken = createAccessToken();

        // 기존 Access Token을 삭제하고 새로운 Access Token과 연결된 Refresh Token 저장
        redisTemplate.delete(accessToken);
        redisTemplate.opsForValue().set(newAccessToken, refreshToken);
        redisTemplate.expire(newAccessToken, ACCESS_TOKEN_EXPIRATION_TIME);
        redisTemplate.opsForValue().set(refreshToken, newAccessToken);
        redisTemplate.expire(refreshToken, tokenService.getREFRESH_TOKEN_EXPIRATION_TIME());

        return newAccessToken;
    }

}

위의 코드에서는 Redis에 저장된 Access Token의 만료시간을 확인하여 만료된 Access Token인 경우 새로운 Access Token을 생성합니다. 그리고 기존 Access Token을 삭제하고 새로운 Access Token과 연결된 Refresh Token을 Redis에 저장합니다.

위 코드를 참고하여 Redis를 이용하여 Access Token과 Refresh Token을 저장하고, Access Token의 만료시간을 확인하는 코드를 작성해보세요.


내저장소 바로가기 luxury515

+ Recent posts