본문 바로가기
Spring Security

Refresh-Token(JWT)을 MySQL DB가 아닌 Redis 캐시에 저장해보자

by asdft 2024. 5. 20.

RedisTemplate

 

@Configuration
public class RedisConfiguration {
    @Value("${spring.redis.host}")
    private String redisHost;
    @Value("${spring.redis.port}")
    private int redisPort;

    @Bean
    public RedisConnectionFactory redisConnectionFactory() {
        return new LettuceConnectionFactory(redisHost, redisPort);
    }
    @Bean
    public RedisTemplate<String, String> redisTemplate() {
        RedisTemplate<String, String> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(redisConnectionFactory());
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setValueSerializer(new StringRedisSerializer());
        return redisTemplate;
    }
}

 

RedisTemplate 설정을 자세히 보면 key와 value에 대해 Serializer를 설정해주는 모습을 볼 수 있다.

 

나는 key와 value 모두 string 타입으로 지정할 것이기 때문에 StringRedisSerializer를 등록해준다.

 

만약 등록해주지 않는다면 "1"이란 key에 대해 value를 세팅해도 "1"이라는 key가 생성되지 않고 다음과 같은 key가 생성된다.

 


[결과]

User 테이블

1. "email"과 "password"를 입력하고 회원가입을 하면 MySQL DB에 회원 정보가 저장된 것을 확인할 수 있다.

 

 

2. "email" 과 "password"를 입력하여 Login을 진행해보자

 

 

C:\Users\MOS>docker exec -it redis bash		// redis 컨테이너 실행
root@2cf6ba3ef807:/data# redis-cli			
127.0.0.1:6379> keys *				// 모든 Key 값 출력
1) "2"
127.0.0.1:6379> get 2				// 해당 Key의 Value, 즉 Refresh-Token 값 출력	
"eyJhbGciOiJIUzUxMiJ9.eyJleHAiOjE3MTY3OTgzOTR9.h_2jk-vSJrngmlHiPssGWun6rHFZmm5L4DODMdojZG2N1GmL1XKZVhMeYUxVqgS3bpN46Mk6DIuqHcc9-Poqww"
127.0.0.1:6379> PTTL 2				// 유효기간: refresh 토큰 만료 시간 (ms)
(integer) 1716798343195				// 해당 시간이 지나면 key와 value가 Redis에서 자동 삭제된다

 

 

RefreshToken 테이블

 

3. redis 컨테이너를 실행 후,  " redis-cli " 명령어를 입력하면 기본적으로 로컬 Redis 서버에 연결한다.

다른 호스트 또는 포트에 연결하려면 '-h' 와 '-p' 옵션을 사용하면 된다.

 

그럼 PostMan에서 확인한 Refresh-Token 값이 MySQL Database가 아닌 Redis 캐시에 저장된 것을 확인할 수 있다.

 

PTTL 의 경우 단위는 밀리 세컨드(ms) 이며, 해당 유효기간이 지나면 Redis 캐시에서 key-value 값이 자동 삭제된다.

 

 

 

4. 2~3번 과정에서 발급 받은 Access-Token과 Refresh_Token 값을 Body에 담아 토큰 재발급(reissue) API로 보내보자.

그럼 새로운 Access-Token과 Refresh-Token을 발급 받을 수 있다.

 

 

127.0.0.1:6379> get 2
"eyJhbGciOiJIUzUxMiJ9.eyJleHAiOjE3MTY3OTkwNDh9.BHbnxNMa293_-Kxgf2QGDP98Bn5UxATRI59pBUeLcEF-W_0b6Mgp9HVLByiHhoAlDpJpBWLBWv3uMVOynuwIgw"
127.0.0.1:6379> PTTL 2
(integer) 1716798898707

 

5. 새로 발급 받은 Refresh-Token이 Redis 캐시에 업데이트 된 것을 확인할 수 있다.

 


[정리]

Redis 사용 명령어

Redis 서버에 연결: redis-cli
키 조회: KEYS *
값 조회: GET key
값 설정: SET key value
데이터베이스 정보: INFO

 

private void saveRefreshTokenCache(Authentication authentication, TokenDto tokenDto) {
    redisTemplate.opsForValue().set(
          authentication.getName(),             // 키: 사용자 이름
          tokenDto.refreshToken(),              // 값: refresh 토큰
          tokenDto.refreshTokenExpiresIn(),     // 유효기간 지나면 Redis에서 자동 삭제
          TimeUnit.MILLISECONDS                 // ms 단위로 저장
    );
  }

 

redisTemplate의 TTL 설정 시 이점

 

- 자동 만료

Redis의 TTL을 설정하면 데이터가 저장된 이후 일정 시간이 지나면 Redis에서 자동으로 삭제됩니다. 이를 통해 만료된 토큰은 더 이상 사용할 수 없으므로 탈취된 토큰의 위험이 크게 감소합니다.

 

- 보안 강화

TTL을 사용하여 토큰의 만료 시간을 명시적으로 설정함으로써, 토큰이 더 이상 필요하지 않을 때 Redis에서 해당 토큰을 자동으로 삭제할 수 있습니다. 이는 보안을 강화하고 불필요한 데이터 유지를 방지할 수 있습니다.

 

- 메모리 관리

만료된 토큰이 자동으로 삭제되면 Redis의 메모리 사용량을 최적화할 수 있습니다. 만약 토큰이 계속해서 유효하지 않은 상태로 남아있다면, 메모리 공간을 낭비하게 될 수 있습니다. TTL을 사용하여 만료된 토큰을 적절히 관리함으로써 메모리 사용을 효율적으로 관리할 수 있습니다.

 

따라서, Redis의 TTL 설정을 사용하여 토큰의 만료를 관리함으로써 토큰 탈취의 위험을 낮출 수 있습니다. 그러나 유효기간을 적절하게 설정하는 것이 중요하며, 보안 요구 사항과 서비스의 요구 사항을 고려하여 설정해야 합니다.

 

그래서 TTL의 시간의 경우 1시간으로 지정했습니다.

 


Refresh-Token을 Mysql이 아닌 Redis 캐시에 저장할 경우 아래와 같이 성능적, 보안적인 장점

 

<성능적 측면>

1. 빠른 응답 시간 및 성능

 

Redis는 메모리 기반의 데이터 저장소이기 때문에 빠른 응답 시간을 제공합니다. 따라서 JWT Refresh Token을 Redis에 저장하면 데이터베이스 쿼리를 실행할 필요 없이 즉시 접근할 수 있습니다. 이는 인증 프로세스의 성능을 향상시킬 수 있습니다.

2. 메모리 최적화

Redis는 메모리 내부에서 데이터를 저장하고 관리하기 때문에 빠른 속도와 함께 메모리를 효율적으로 사용할 수 있습니다. 따라서 대량의 토큰을 캐싱하더라도 성능에 부담을 주지 않고 메모리를 최적화할 수 있습니다.

 

 

<보안적 측면>

1. 제한된 범위의 데이터 저장소

 

Redis는 메모리 기반의 데이터 저장소이기 때문에 주로 캐시나 세션 관리 등의 용도로 사용됩니다. 따라서 Redis에 저장된 데이터는 보통 짧은 시간 동안만 유지됩니다. JWT Refresh Token과 같은 중요한 보안 정보를 Redis에 저장하면 해당 토큰이 유출되더라도 공격자가 접근할 수 있는 시간이 제한되어 있어 보안 위험이 감소합니다.

2. 만료 시간 설정

Redis는 TTL(Time To Live)을 설정하여 데이터를 자동으로 만료시킬 수 있습니다. JWT Refresh Token을 저장할 때 토큰의 만료 시간을 설정하여 만료된 토큰은 자동으로 삭제될 수 있습니다. 이를 통해 만료된 토큰을 악용하는 공격을 방지할 수 있습니다.

3. 안전한 통신 채널

Redis는 기본적으로 클라이언트와 서버 간의 통신을 위해 안전한 TLS/SSL 연결을 사용할 수 있습니다. 따라서 Redis를 사용하여 JWT Refresh Token을 저장하면 통신 과정에서 토큰이 노출되거나 변조되는 것을 방지할 수 있습니다.