33import com .Timo .Timo .global .jwt .provider .JwtTokenProvider ;
44import java .util .ArrayList ;
55import java .util .List ;
6- import java .util .Objects ;
7- import java .util .Set ;
86import java .util .UUID ;
97import java .util .concurrent .TimeUnit ;
108import lombok .RequiredArgsConstructor ;
119import org .springframework .data .redis .core .Cursor ;
1210import org .springframework .data .redis .core .RedisTemplate ;
1311import org .springframework .data .redis .core .ScanOptions ;
12+ import org .springframework .data .redis .core .script .DefaultRedisScript ;
13+ import org .springframework .data .redis .core .script .RedisScript ;
1414import org .springframework .stereotype .Service ;
1515
1616@ Service
@@ -22,23 +22,30 @@ public class RefreshTokenService {
2222
2323 private static final String KEY_PREFIX = "refresh:" ;
2424
25+ private static final RedisScript <Long > COMPARE_AND_DELETE_SCRIPT = new DefaultRedisScript <>(
26+ "local stored = redis.call('GET', KEYS[1]) "
27+ + "if stored == ARGV[1] then "
28+ + " redis.call('DEL', KEYS[1]) "
29+ + " return 1 "
30+ + "else "
31+ + " return 0 "
32+ + "end" ,
33+ Long .class
34+ );
35+
2536 public String saveRefreshToken (String userId , String refreshToken ){
2637 String sessionId = UUID .randomUUID ().toString ();
2738 redisTemplate .opsForValue ().set (
28- KEY_PREFIX + userId + ":" + sessionId ,
39+ buildKey ( userId , sessionId ) ,
2940 refreshToken ,
3041 jwtTokenProvider .getRefreshTokenExpiry (),
3142 TimeUnit .SECONDS
3243 );
3344 return sessionId ;
3445 }
3546
36- public String getRefreshToken (String userId , String sessionId ) {
37- return redisTemplate .opsForValue ().get (KEY_PREFIX + userId + ":" + sessionId );
38- }
39-
4047 public void deleteRefreshToken (String userId , String sessionId ) {
41- redisTemplate .delete (KEY_PREFIX + userId + ":" + sessionId );
48+ redisTemplate .delete (buildKey ( userId , sessionId ) );
4249 }
4350
4451 public void deleteAllRefreshTokens (String userId ) {
@@ -60,7 +67,16 @@ public void deleteAllRefreshTokens(String userId) {
6067 }
6168 }
6269
63- public boolean isRefreshTokenValid (String userId , String sessionId , String refreshToken ) {
64- return Objects .equals (refreshToken , getRefreshToken (userId , sessionId ));
70+ public boolean validateAndConsumeRefreshToken (String userId , String sessionId , String refreshToken ) {
71+ Long result = redisTemplate .execute (
72+ COMPARE_AND_DELETE_SCRIPT ,
73+ List .of (buildKey (userId , sessionId )),
74+ refreshToken
75+ );
76+ return Long .valueOf (1L ).equals (result );
77+ }
78+
79+ private String buildKey (String userId , String sessionId ) {
80+ return KEY_PREFIX + userId + ":" + sessionId ;
6581 }
6682}
0 commit comments