日本免费高清视频-国产福利视频导航-黄色在线播放国产-天天操天天操天天操天天操|www.shdianci.com

學無先后,達者為師

網站首頁 編程語言 正文

基于 Redis 的 JWT令牌失效方案

作者:Mr.VK 更新時間: 2024-03-09 編程語言

應用場景

當用戶登錄狀態到登出狀態時,對應的JWT的令牌需要設置為失效狀態,這時可以使用基于 Redis 的黑名單方案來實現JWT令牌失效。

基于 Redis 的黑名單方案

當用戶需要登出系統時,將用戶攜帶的Token進行解析,解碼出JWT令牌,取出對應的 UUID 過期時間 ,用過期的時間減去當前的時間,計算出這個Key的過期時間,再以這兩個字段拼接作為 Key 并設置好過期時間存儲到 Redis 中,如果有黑客拿竊取出來的JWT令牌進行登錄,只要判斷這個JWT令牌是否在黑名單就可以。

實現步驟

1.獲得攜帶的Token解析并取出JWT令牌的代碼

這段代碼實現了對指定 JWT 的驗證和使令牌失效的操作。

  1. 首先,通過調用 convertToken(headerToken) 方法將傳入的頭部令牌 headerToken 轉換成實際的 JWT 字符串 token。
  2. 然后,使用 HMAC256 算法和預設的密鑰 key 創建一個算法實例 algorithm。
  3. 接下來,使用算法實例 algorithm 構建一個 JWT 驗證器 jwtVerifier。這個驗證器將用于驗證 JWT 的有效性。
  4. 在 try-catch 塊中,首先通過調用 jwtVerifier.verify(token) 方法對 JWT 進行驗證。如果驗證成功,則返回一個 DecodedJWT 對象 verify,其中包含了 JWT 的解碼信息,如令牌的唯一標識符(ID)和過期時間等。
  5. 接著,調用 deleteToken(verify.getId(), verify.getExpiresAt()) 方法來刪除指定令牌,并將其加入到黑名單中進行失效處理。這里使用了 verify 對象中的 ID 和過期時間作為參數。
  6. 最后,如果在驗證 JWT 過程中發生了 JWTVerificationException 異常,即 JWT 驗證失敗,則捕獲該異常,并返回 false 表示令牌失效操作失敗。
    /**
     * 讓指定Jwt令牌失效
     * @param headerToken 請求頭中攜帶的令牌
     * @return 是否操作成功
     */
    public boolean invalidateJwt(String headerToken){
        String token = this.convertToken(headerToken);
        Algorithm algorithm = Algorithm.HMAC256(key);
        JWTVerifier jwtVerifier = JWT.require(algorithm).build();
        try {
            DecodedJWT verify = jwtVerifier.verify(token);
            return deleteToken(verify.getId(), verify.getExpiresAt());
        } catch (JWTVerificationException e) {
            return false;
        }
    }

2.檢查指定 UUID 的令牌是否為無效的(已加入黑名單)

這段代碼用于檢查指定 UUID 的令牌是否為無效的(已加入黑名單),通過判斷 Redis 數據庫中是否存在相應的鍵來決定令牌的有效性。如果鍵存在,則表示令牌已失效;如果鍵不存在,則表示令牌仍然有效。

    /**
     * 驗證Token是否被列入Redis黑名單
     * @param uuid 令牌ID
     * @return 是否操作成功
     */
    private boolean isInvalidToken(String uuid){
        return Boolean.TRUE.equals(template.hasKey(Const.JWT_BLACK_LIST + uuid));
    }

3.將Token列入Redis黑名單中

這段代碼實現了對指定令牌的刪除和加入黑名單的操作,用于管理令牌的有效性和安全性。

  1. 首先,通過調用 isInvalidToken(uuid) 方法來檢查指定的 UUID 是否為無效的令牌。如果 isInvalidToken 方法返回 true,則說明該令牌無效,此時直接返回 false,不執行后續操作。
  2. 獲取當前時間 now,然后計算令牌的過期時間與當前時間的差值,并取最大值作為令牌的失效時間 expire。這里使用了 Math.max 方法來確保失效時間不會小于 0。
  3. 最后,通過 Redis 的 template 對象調用 opsForValue().set() 方法,將指定 UUID 的令牌加入到名為 Const.JWT_BLACK_LIST + uuid 的鍵中,并設置過期時間為 expire 毫秒。這樣就將該令牌加入到了黑名單中,使其在一定時間后失效。
  4. 最終,方法返回 true 表示成功刪除令牌并將其加入黑名單。
    /**
     * 將Token列入Redis黑名單中
     * @param uuid 令牌ID
     * @param time 過期時間
     * @return 是否操作成功
     */
    private boolean deleteToken(String uuid, Date time){
        if(this.isInvalidToken(uuid))
            return false;
        Date now = new Date();
        long expire = Math.max(time.getTime() - now.getTime(), 0);
        template.opsForValue().set(Const.JWT_BLACK_LIST + uuid, "", expire, TimeUnit.MILLISECONDS);
        return true;
    }
public final class Const {
    //JWT令牌
    public final static String JWT_BLACK_LIST = "jwt:blacklist:";
    public final static String JWT_FREQUENCY = "jwt:frequency:";
}

對應完整的代碼如下:

@Component
public class JwtUtils {

    @Autowired
    private StringRedisTemplate template;

    @Value("${spring.security.jwt.key}")
    String key;

    @Value("${spring.security.jwt.expire}")
    int expire;

    /**
     * 讓指定Jwt令牌失效
     * @param headerToken 請求頭中攜帶的令牌
     * @return 是否操作成功
     */
    public boolean invalidateJwt(String headerToken){
        String token = this.convertToken(headerToken);
        Algorithm algorithm = Algorithm.HMAC256(key);
        JWTVerifier jwtVerifier = JWT.require(algorithm).build();
        try {
            DecodedJWT verify = jwtVerifier.verify(token);
            return deleteToken(verify.getId(), verify.getExpiresAt());
        } catch (JWTVerificationException e) {
            return false;
        }
    }

    /**
     * 將Token列入Redis黑名單中
     * @param uuid 令牌ID
     * @param time 過期時間
     * @return 是否操作成功
     */
    private boolean deleteToken(String uuid, Date time){
        if(this.isInvalidToken(uuid))
            return false;
        Date now = new Date();
        long expire = Math.max(time.getTime() - now.getTime(), 0);
        template.opsForValue().set(Const.JWT_BLACK_LIST + uuid, "", expire, TimeUnit.MILLISECONDS);
        return true;
    }

    /**
     * 驗證Token是否被列入Redis黑名單
     * @param uuid 令牌ID
     * @return 是否操作成功
     */
    private boolean isInvalidToken(String uuid){
        return Boolean.TRUE.equals(template.hasKey(Const.JWT_BLACK_LIST + uuid));
    }

    public DecodedJWT resolveJwt(String headerToken) {
        String token = this.convertToken(headerToken);
        if (token == null) {
            return null;
        }
        Algorithm algorithm = Algorithm.HMAC256(key);
        JWTVerifier jwtVerifier = JWT.require(algorithm).build();
        try {
            DecodedJWT verify = jwtVerifier.verify(token);
            if(this.isInvalidToken(verify.getId())) return null;
            Date expireAt = verify.getExpiresAt();
            return new Date().after(expireAt) ? null : verify;
        } catch (JWTVerificationException e) {
            return null;
        }
    }

    public UserDetails toUser(DecodedJWT jwt) {
        Map<String, Claim> claims = jwt.getClaims();
        return User.withUsername(claims.get("name").asString())
                .password("********")
                .authorities(claims.get("authorities").asArray(String.class))
                .build();
    }

    public Integer toId(DecodedJWT jwt) {
        Map<String, Claim> claims = jwt.getClaims();
        return claims.get("id").asInt();
    }

    public String createJwt(UserDetails details, int id, String username) {
        Algorithm algorithm = Algorithm.HMAC256(key);
        Date expire = this.expireTime();
        return JWT.create()
                .withJWTId(UUID.randomUUID().toString())
                .withClaim("id", id)
                .withClaim("name", username)
                .withClaim("authorities", details.getAuthorities().stream().map(GrantedAuthority::getAuthority).toList())
                .withExpiresAt(expire)
                .withIssuedAt(new Date())
                .sign(algorithm);
    }

    public Date expireTime() {
        Calendar calendar = Calendar.getInstance();
        calendar.add(Calendar.HOUR, expire * 24);
        return calendar.getTime();
    }

    private String convertToken(String headerToken) {
        if(headerToken == null || !headerToken.startsWith("Bearer ")) {
            return null;
        }
        return headerToken.substring(7);
    }
}

原文鏈接:https://blog.csdn.net/Mr_VK/article/details/136456365

  • 上一篇:沒有了
  • 下一篇:沒有了
欄目分類
最近更新