基于 Redis 的 JWT令牌失效方案

应用场景

当用户登录状态到登出状态时,对应的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 {@Autowiredprivate 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);}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/720361.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Go语言中的时间控制:定时器技术详细指南

Go语言中的时间控制&#xff1a;定时器技术详细指南 引言定时器基础创建和使用time.Timer使用time.Ticker实现周期性任务定时器的内部机制小结 使用time.Timer实现简单的定时任务创建和启动定时器停止和重置定时器定时器的实际应用小结 利用time.Ticker处理重复的定时任务创建和…

这里推荐一款unity3d人物动物控制器详细的等学会再写文章

unity3d Animal Controller 1.4.0a 动物NPC行为控制器 动物控制器&#xff08;AC&#xff09;是一个基于脚本架构的动画框架控制器。它适用于任何动物或人形角色的根运动或原地动画。 人和动物的各种动作都有; 小白必选、 我只是运行乐demo就感觉牛 demo路径&#xff1a;Asset…

Python不换行print在终端中不显示

问题描述 当使用不换行 print 即 print(‘test, end) 后立即关闭标准输出 sys.stdout open(os.devnull, w)则 print 的内容不会显示在正常的终端上&#xff08;例外是 PyCharm 中的终端能够正常显示&#xff09;。 复现问题 复现该问题的简易代码&#xff1a; import sys,…

基于pytorch的手写体识别

一、环境搭建 链接: python与深度学习——基础环境搭建 二、数据集准备 本次实验用的是MINIST数据集&#xff0c;利用MINIST数据集进行卷积神经网络的学习&#xff0c;就类似于学习单片机的点灯实验&#xff0c;学习一门机器语言输出hello world。MINIST数据集&#xff0c;可以…

【go从入门到精通】go环境安装和第一个经典程序

go下载和环境变量配置 下载地址 Go官网下载地址&#xff1a;https://golang.org/dl/All releases - The Go Programming Languagehttps://golang.org/dl/ 然后根据自己的系统环境来选择不同的安装包下载&#xff0c;下面我分别针对不同环境进行说明&#xff08;大家可以根据自…

计算机网络中常用的命令

Ping 通过发送一系列的ICMP回送数据报来确定本机与目的主机是否可以正常通信。输出中TTL代表ICMP报文存活时间&#xff0c;可以推算报文经过了几个路由器。ping失败有可能是由于Windows防火墙-IPv4回显请求没有开启。Ipconfig 获取主机的网络配置信息&#xff0c;例如IP地址、子…

LeetCode 438. 找到字符串中所有字母异位词

对于判断两个词是否为异位词&#xff0c;可以改而判断它们的词频表是否相同。基于此&#xff0c;在s串中设置滑动窗口&#xff0c;大小跟p串一样&#xff0c;移动&#xff08;剔除左边&#xff0c;增加右边&#xff09;这个窗口并实时记录下它的词频表然后与p的词频表比较。 cl…

数据库之Oracle数据导入导出

目录 一、单表导出和导入1、单表导出数据2、单表导入数据二、全表导出和导入1、远程导出全表数据2、导入本地数据三、密码带特殊字符的写法1、Windows OS写法2、Linux/Unix OS写法 四、总结 一、单表导出和导入 1、单表导出数据 --导出远程服务上的表数据 exp 用户名/密码IP…

Platformview在iOS与Android上的实现方式对比

Android中早期版本Platformview的实现基于Virtual Display。VirtualDisplay方案的原理是&#xff0c;先将Native View绘制到虚显&#xff0c;然后Flutter通过从虚显输出中获取纹理并将其与自己内部的widget树进行合成&#xff0c;最后作为Flutter在 Android 上更大的纹理输出的…

【java】redis的bitmap实现签到功能

功能&#xff1a; 1.签到 2.判断某天是否签到 3.统计某月的签到情况 4.统计某月连续签到了多少天 5.统计某月一共签到了多少次 代码 1.依赖 <dependencies><dependency><groupId>redis.clients</groupId><artifactId>jedis</artifactId>…

Unity2023.1.19_ECS_DOTS

Unity2023.1.19_ECS_DOTS 盲学-盲目的学习&#xff1a; 懒着自己整理就看看别人整理的吧&#xff0c;整合一下逻辑通了不少&#xff1a; DOTS/data oriented technology stack-面向数据的技术栈 ECS/Entities-Component-System Unity-Entities包 Entities提供ECS架构面向数…

BUUCTF---[ACTF2020 新生赛]BackupFile1

1.题目描述 2.题目提示backup file &#xff0c;是备份文件的意思。点开链接&#xff0c;页面提示 3.查看源码没有什么有用信息&#xff0c;也没有登录界面&#xff0c;所以也不会用到蚁剑链接来找备份文件&#xff0c;所以大概率就是通过构造playload来查找备份文件。 4.备份…

kubectl 速查手册

资源对象文件 --- kind: Pod apiVersion: v1 metadata:name: myweblabels:app: nginx spec:containers:- name: webserverimage: nginx status: {} annotate # 更新资源所关联的注释信息 #-----------------------------------------# [rootmaster k8s]# kubectl apply -f m…

图像检测 - 模板匹配方法(算法详解)

1、介绍 在如今深度学习、神经网络遍地的世界,检测已经很常见了。在深度学习领域,关于目标检测也提出了很多很成熟的算法,例如双阶段的Fast-RCNN、单阶段的YOLO系列等等。 尤其是YOLO系列的不断更新迭代,目标检测的任务变得不再困难,可以做到实时检测的跟踪等等 但对于数…

新手想玩硬件,买单片机还是树莓派好?

新手想玩硬件&#xff0c;买单片机还是树莓派好&#xff1f; 在开始前我有一些资料&#xff0c;是我根据网友给的问题精心整理了一份「单片机的资料从专业入门到高级教程」&#xff0c; 点个关注在评论区回复“888”之后私信回复“888”&#xff0c;全部无偿共享给大家&#x…

Springboot+vue的船舶监造系统(有报告)。Javaee项目,springboot vue前后端分离项目。

演示视频&#xff1a; Springbootvue的船舶监造系统&#xff08;有报告&#xff09;。Javaee项目&#xff0c;springboot vue前后端分离项目。 项目介绍&#xff1a; 本文设计了一个基于Springbootvue的船舶监造系统&#xff0c;采用M&#xff08;model&#xff09;V&#xff…

二百二十六、Linux——shell脚本查看今天日期、昨天日期、30天前日期、1月前日期

一、目的 由于磁盘资源有限&#xff0c;因为对原始数据的保存有事件限制&#xff0c;因为对于超过一定期限的数据文件则需要删除&#xff0c;要实现定期删除则第一步就是查看日期时间 二、在Linux中创建shell脚本 #! /bin/bash source /etc/profile nowdatedate --date0 da…

Linux 学习笔记(11)

十一、 资源监控 1 、 free 内存监控 语 法&#xff1a; free [-bkmotV][-s < 间隔秒数 >] 补充说明&#xff1a; free 指令会显示内存的使用情况&#xff0c;包括实体内存&#xff0c;虚拟的交换文件内存&#xff0c;共享内存区段&#xff0c;以 及系统核心使用的…

[计算机网络]:流量控制

一、流量控制简介 一条TCP连接的每一侧主机都为其设置了接收缓存&#xff0c;当TCP成功连接后&#xff0c;它发送的数据会放入接受缓存中。相关联的进程会从缓存中读取数据。但是存在一个问题&#xff0c;当某应用程序读取数据速率太慢&#xff0c;而发送数据一方不停的发送数…

5. 【Codeforces Round 927 (Div. 3)】B.查亚日历

B . 查亚日历 B.查亚日历 B.查亚日历 每次测试时限&#xff1a; 2 秒 每次测试时限&#xff1a;2 秒 每次测试时限&#xff1a;2秒 每次测试的内存限制&#xff1a; 256 兆字节 每次测试的内存限制&#xff1a;256 兆字节 每次测试的内存限制&#xff1a;256兆字节 题目描述 查…