RedisによるJWT Blacklistの実装


jwtベースのユーザ認証を実施する際にログアウトを実施する必要があります.
Access TokenとRefresh Tokenは簡単に削除できます.
問題は、Access Tokenの有効期間がまだ有効で、誰かがログアウトしている場合でも使用できます.
もちろん、有効期限は短い(30分)ですが、Access TokenをBlacklistとして保存して有効期限を切れさせる機能を実装したいと思います!

Redisとは?


公式ホームページの説明
  • Redisは、ソースコードを迅速にオープンするメモリキー値データ構造記憶領域である.Redisは異なるメモリデータ構造のセットを提供するため、さまざまなカスタムアプリケーションを簡単に作成できます.主なRedisの使用例には、キャッシュ、セッション管理、pub/sub、ランキングが含まれます.Redisは現在最も人気のあるキーストアで、BSDライセンスを持ち、最適化されたCコードで作成され、多くの開発言語をサポートしています.
  • 今回プロジェクトをした時に初めて知り合った子供.
    メモリ・データ・リポジトリであり、キャッシュ、セッション管理などによく使用されます.
    これらの特性を利用してBlacklistを実装したい.

    スプリングにRedisを使う


    依存性

    implementation("org.springframework.boot:spring-boot-starter-data-redis")

    bean登録

    @Configuration
    public class RedisConfig {
    
        @Value("${spring.redis.host}")
        private String host;
    
        @Value("${spring.redis.port}")
        private int port;
    
        @Bean
        public LettuceConnectionFactory redisConnectionFactory() {
            return new LettuceConnectionFactory(host, port);
        }
    
        @Bean
        public RedisTemplate<String, Object> redisTemplate() {
            RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
            redisTemplate.setKeySerializer(new StringRedisSerializer());
            redisTemplate.setValueSerializer(new StringRedisSerializer());
            redisTemplate.setConnectionFactory(redisConnectionFactory());
            return redisTemplate;
        }
    }
    形式のTemplateを作成し、必要なフォーマットがあれば追加してbeanとして登録できます!
    cache機能、redisConnectionFactoryフラクチャなど、他の機能を追加することもできます.

    ログアウト機能

    // AuthService
    @Transactional
    public void logout(String accessToken, String refreshToken) {
    	// 1. Access Token 검증
        if (!tokenProvider.validateToken(accessToken)) {
        	throw new ApiException(BasicResponseMessage.UNAUTHORIZED);
        }
    
    	// 2. Access Token 에서 authentication 을 가져옵니다.
        Authentication authentication = tokenProvider.getAuthentication(accessToken);
    
    	// 3. DB에 저장된 Refresh Token 제거
        Long userId = Long.parseLong(authentication.getName());
        refreshTokenRepository.deleteById(userId);
    
    	// 4. Access Token blacklist에 등록하여 만료시키기
        // 해당 엑세스 토큰의 남은 유효시간을 얻음
    	Long expiration = tokenProvider.getExpiration(accessToken);	
    	redisUtil.setBlackList(accessToken, "access_token", expiration);
    }
    実際にログアウトして、データベースに格納されているRefreshTokenを削除します.
    BlacklistにAccess Tokenを登録します.

    ブラックリスト機能

    @Component
    @RequiredArgsConstructor
    public class RedisUtil {
        private final RedisTemplate<String, Object> redisTemplate;
        private final RedisTemplate<String, Object> redisBlackListTemplate;
    
        public void set(String key, Object o, int minutes) {
            redisTemplate.setValueSerializer(new Jackson2JsonRedisSerializer(o.getClass()));
            redisTemplate.opsForValue().set(key, o, minutes, TimeUnit.MINUTES);
        }
    
        public Object get(String key) {
            return redisTemplate.opsForValue().get(key);
        }
    
        public boolean delete(String key) {
            return redisTemplate.delete(key);
        }
    
        public boolean hasKey(String key) {
            return redisTemplate.hasKey(key);
        }
    
        public void setBlackList(String key, Object o, Long milliSeconds) {
            redisBlackListTemplate.setValueSerializer(new Jackson2JsonRedisSerializer(o.getClass()));
            redisBlackListTemplate.opsForValue().set(key, o, milliSeconds, TimeUnit.MILLISECONDS);
        }
    
        public Object getBlackList(String key) {
            return redisBlackListTemplate.opsForValue().get(key);
        }
    
        public boolean deleteBlackList(String key) {
            return redisBlackListTemplate.delete(key);
        }
    
        public boolean hasKeyBlackList(String key) {
            return redisBlackListTemplate.hasKey(key);
        }
    }
    Blacklistの登録は非常に簡単です.RedisTemplateにだけあります.
    登録するAccess Token、Objectの値と有効時間を入力します.
    う!!!いいえ、このように登録されています.
    Access Tokenを受信するたびにBlacklistが存在するかどうかをチェックするだけです.

    ブラックリストが存在するかどうかを確認(ログアウト済みかどうかを確認)

    // TokenProvider
    public boolean validateToken(String token) {
    	try {
        	Jwts.parserBuilder().setSigningKey(key).build().parseClaimsJws(token);
            if(redisUtil.hasKeyBlackList(token)) {
            	return false;
            }
            return true;
        } catch (io.jsonwebtoken.security.SecurityException | MalformedJwtException e) {
        	....
        }        
    }
    既存のJWT検証を行う場合は、TokenがBlacklistに追加されていることを確認し、検証を行います.
    今は本当に终わりました!!!
    そうかと思ったらハハハハ
    私はそれをこの状態に変えて、間違いを発見しました.redisconnectionfailureexception unable to connect to redisこんなミスがあった瞬間???問題が起こると思っていた.
    ホストとポートの指定を考えてもRedisプログラムをインストールして回るのでしょうか?
    本当に取り付けて回す必要があることを知りました.
    私は本当に馬鹿です.

    Dockerを使用してRedisをインストールして実行

    docker run -i -t --name redis -p 6379:6379 redis:alpineこれだけ自分でRedisimageをダウンロードして実行
    バックグラウンドで実行したい場合は、-dオプションを追加できます!
    私が運転してReidsに戻ったところ、logout機能が正常に動作していることがわかりました!

    に感銘を与える


    コードが簡単だとは思わなかった.
    Redisを設定していないだけで少し笑わせる
    Dockerとして簡単にインストールおよび実行できます.
    実際のサーバに配備したり、事前にインストールしたりすることができます.
    ユーザー認証が安全に行えるようになりました!!

    コメントサイト

  • https://wnwngus.tistory.com/65?category=894390
  • https://velog.io/@kenux/Redis-%EC%8A%A4%ED%94%84%EB%A7%81-%EB%B6%80%ED%8A%B8-Redis-%EC%82%AC%EC%9A%A9%ED%95%B4%EB%B3%B4%EA%B8%B0