Redis

关注公众号 jb51net

关闭
首页 > 数据库 > Redis > Redis  JWT令牌

基于 Redis 的 JWT令牌失效处理方案(实现步骤)

作者:Mr.VK

当用户登录状态到登出状态时,对应的JWT的令牌需要设置为失效状态,这时可以使用基于Redis 的黑名单方案来实现JWT令牌失效,本文给大家分享基于 Redis 的 JWT令牌失效处理方案,感兴趣的朋友一起看看吧

应用场景

当用户登录状态到登出状态时,对应的JWT的令牌需要设置为失效状态,这时可以使用基于 Redis 的黑名单方案来实现JWT令牌失效。

基于 Redis 的黑名单方案

当用户需要登出系统时,将用户携带的Token进行解析,解码出JWT令牌,取出对应的 UUID 过期时间 ,用过期的时间减去当前的时间,计算出这个Key的过期时间,再以这两个字段拼接作为 Key 并设置好过期时间存储到 Redis 中,如果有黑客拿窃取出来的JWT令牌进行登录,只要判断这个JWT令牌是否在黑名单就可以。

实现步骤

1.获得携带的Token解析并取出JWT令牌的代码

这段代码实现了对指定 JWT 的验证和使令牌失效的操作。

    /**
     * 让指定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黑名单中

这段代码实现了对指定令牌的删除和加入黑名单的操作,用于管理令牌的有效性和安全性。

    /**
     * 将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);
    }
}

到此这篇关于基于 Redis 的 JWT令牌失效方案的文章就介绍到这了,更多相关Redis JWT令牌失效内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文