Redis - Token JWT 概念解析及双token实现分布式session存储实战

Token

  1. 定义:令牌,访问资源接口(API)时所需要的资源凭证

一、Access Token

  1. 定义:访问资源接口(API)时所需要的资源凭证,存储在客户端

  2. 组成

    组成部分说明
    uid用户唯一的身份标识
    time当前时间的时间戳
    sign签名,token 的前几位以哈希算法压缩成的一定长度的十六进制字符串
  3. 验证流程

    1. 登录:客户端使用 username & password 请求登录
    2. 验证:服务端收到请求,验证 username & password
      1. 验证成功 → 服务端会签发一个 token 并把这个 token 发送给客户端
      2. 验证失败 → 登录失败
    3. 存储 token:客户端收到 token 以后,会把它存储起来,比如放在 cookie 里或者 localStorage 里
    4. 携带 token:客户端每次向服务端请求资源的时候需要携带服务端签发的 token(放在 HTTP 的 Header 中)
    5. 解析 token:服务端收到请求,解析客户端的 token 数据
      1. 验证成功 → 向客户端返回请求的数据
      2. 验证失败 → 拒绝请求,要求重新登录

二、Refresh Token

  1. 定义:专用于刷新 access token 的 token

  2. 功能:减少重复登录操作,Access Token 失效时,客户端直接用 refresh token 去更新 access token,无需用户进行额外的操作

  3. 存储位置:服务器的数据库

  4. 工作流程


JWT

一、概述

  1. 定义:JSON Web Token(简称 JWT),一种认证授权机制,是目前最流行的跨域认证解决方案
  2. 功能:实现跨域请求,用户只要在其中一个网站登录,再访问另一个网站就会自动登录(session 所有数据都保存在客户端,每次请求都发回服务器)
  3. 存储位置:HTTP 请求的头信息 Authorization 字段中,格式为:Authorization: Bearer <token>
  4. 基本格式:(Header.Payload.Signature)

二、组成部分

  1. Header

    1. 定义:描述 JWT 的元数据(配置信息),记录令牌类型、签名算法等配置,由 Base64URL 算法转为字符串

    2. 示例

      {
        "alg": "HS256",        // 签名算法类型
        "typ": "JWT"           // Token类型
      }
      
  2. Payload

    1. 定义:记录用户信息的数据(不是加密数据,不能存敏感信息)

    2. 示例

      {
        "sub": "1234567890",
        "name": "John Doe",
        "admin": true
      }
      
  3. Signature

    1. 定义:对前两部分(Header和Payload)的数字签名,用于验证消息的完整性和确保数据未被篡改。这是JWT安全性的核心保障

    2. 生成过程:

      1. 服务器持有一个密钥(secret),该密钥必须妥善保管且不能泄露
      2. 使用Header中指定的签名算法(默认为HMAC SHA256)
      3. 将编码后的Header和Payload用"."连接,再使用密钥和签名算法生成签名
    3. 获取签名方式:

      HMACSHA256(
        base64UrlEncode(header) + "." +
        base64UrlEncode(payload),
        secret)
      

三、优缺点

  1. 优点
    1. JWT 是自包含的(内部包含了一些会话信息),因此减少了查询数据库的需要,有效使用 JWT,可以降低服务器查询数据库的次数
    2. JWT 不仅可以用于认证,也可以用于交换信息
    3. JWT 并不使用 Cookie 的,所以可以使用任何域名提供你的 API 服务而不需要担心跨域资源共享问题(CORS)
  2. 缺点
    1. JWT 的最大缺点:由于服务器不保存 session 状态,因此无法在使用过程中废止或更改某个 token 的权限。一旦 JWT 签发,在到期之前就会始终有效
    2. JWT 默认不加密,但也是可以加密(生成原始 Token 以后,可以用密钥再加密一次)
    3. JWT 不加密的情况下,不能将秘密数据写入 JWT
    4. JWT 本身包含了认证信息,一旦泄露,任何人都可以通过 JWT 获得该 JWT 的所有权限

四、工作流程



代码实现

⭐参考资料:

  1. https://blog.csdn.net/gitblog_09788/article/details/143407938#:~:text=1 JWT集成:生成安全的JWT令牌,用于用户身份验证。 2 Redis存储:将刷新令牌存储在Redis中,利用其高速特性进行快速校验。 3 双Token机制:,访问令牌:短寿命,用于直接的API访问。 刷新令牌:长寿命,用于在访问令牌过期时自动获取新令牌。 4 自动刷新:当访问令牌过期时,系统可自动利用有效的刷新令牌获取新的访问令牌。 5 安全性强化:通过Redis的过期策略、JWT的签名验证以及适当的访问控制,增强系统的安全性。
  2. GitHub - dolyw/ShiroJwt: API SpringBoot + Shiro + Java-Jwt + Redis(Jedis) 

方案设计

  1. 方案对比

    特性JWT + Redis 白名单JWT + Redis 存储用户会话
    设计理念无状态为主,Redis 仅辅助校验强状态管理,Redis 为中心,JWT 辅助
    Redis 存储压力存储 jti 或少量数据,存储压力小存储完整会话信息,存储占用较高
    实现复杂度较低,直接存储 jti 和过期时间,校验简单较高,需要设计用户会话结构、处理多端登录等逻辑
    主动失效能力易实现:只需从 Redis 删除对应的 jti 即可易实现:删除会话即可失效
    会话扩展能力较弱,只适合验证 Token 是否有效强,可以存储用户登录的扩展信息(设备、角色等)
    支持多端登录较弱,需要额外逻辑强,天然支持多个会话实例
    性能开销性能更高(JWT 主要靠自包含验证,少量 Redis 查询)Redis 频繁交互性能略低,适合中等并发的场景
    业务需求复杂度简单业务场景复杂业务场景
  2. 方案选择:JWT + Redis 存储用户会话,可以存储更多用户信息,并且没有泄露风险

校验请求

  1. 目标:过滤所有 token 为空或不合法的请求

  2. 代码(com.lloop.authcheckdemo.interceptor.UserLoginFilter)

    @Slf4j
    @Component
    public class UserLoginFilter implements HandlerInterceptor {
    
        @Resource
        JwtUtils jwtUtils;
    
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
            // 1. 获取token
            String token = request.getHeader(jwtUtils.header);
    
            // 2. 判断token是否有效
            ThrowUtils.throwIf(StringUtils.isEmpty(token), ErrorCode.NULL_ERROR, "请登录后操作!");
            ThrowUtils.throwIf(jwtUtils.isTokenExpired(token), ErrorCode.PARAMS_ERROR, "登录已过期!");
            ThrowUtils.throwIf(jwtUtils.checkBlacklist(token), ErrorCode.PARAMS_ERROR, "用户已被禁止登录!");
    
            // 3. token有效 => 记录登录用户信息
            UserTokenInfo userTokenInfo = jwtUtils.getUserInfoToken(token);
            ThrowUtils.throwIf(ObjectUtils.isEmpty(userTokenInfo), ErrorCode.NULL_ERROR, "对不起,身份认证出现错误,请重新登录...");
            UserHolder.saveUser(userTokenInfo);
    
            return true;
        }
    
        /**
         * 移除用户信息,防止内存溢出
         * @param request
         * @param response
         * @param handler
         * @param ex
         * @throws Exception
         */
        @Override
        public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
            UserHolder.removeUser();
        }
    
    }
    

注册拦截器

  1. 目标:将校验请求的拦截器注册到项目中

  2. 注意:只需要匹配 controller 的路径部分,server.servlet.context-path 不用管

  3. 代码

    @Configuration
    public class WebConfig implements WebMvcConfigurer {
    
        @Resource
        private UserLoginFilter userLoginFilter;
    
        @Override
        public void addInterceptors(InterceptorRegistry registry) {
            registry.addInterceptor(userLoginFilter)
                    .addPathPatterns("/**")
                    .excludePathPatterns("/login", "/register");
        }
    
    }
    

创建用户信息类

  1. 目标:解析用户token中存储的信息,存入 UserHolder 中供 Controller 调用

  2. 代码

    @Data
    public class UserTokenInfo {
    
        /**
         * ID,唯一
         */
        private Long id;
    
        /**
         * 账号
         */
        private String account;
    
        /**
         * 用户昵称
         */
        private String username;
    
        /**
         * 用户角色 0 - 普通用户 1 - 管理员
         */
        private Integer role;
    
    }
    

创建JWT工具类

  1. 目标:创建、校验、解析 token

  2. 代码

    @Data
    @Component
    public class JwtUtils {
    
        @Value("${jwt.secret}")
        public String secret;
    
        @Value("${jwt.header}")
        public String header;
    
        @Value("${jwt.expire.accessToken}")
        public Integer accessTokenExpire;
    
        @Value("${jwt.expire.refreshToken}")
        public Integer refreshTokenExpire;
    
        @Resource
        RedisUtils redisUtils;
    
        private static final Gson gson = new Gson();
        
        
        /**
         * 获取用户信息
         *
         * @param token
         * @return
         */
        public UserTokenInfo getUserInfoToken(String token) {
            String subject = getTokenClaim(token).getSubject();
            UserTokenInfo userTokenInfo = gson.fromJson(subject, UserTokenInfo.class);
            return userTokenInfo;
        }
        
        /**
         * 获取 token 中注册信息
         *
         * @param token
         * @return
         */
        public Claims getTokenClaim(String token) {
            try {
                return Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
            } catch (Exception e) {
                return null;
            }
        }
        
    		
    }
    

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

集成RabbitMQ+MQ常用操作

文章目录 1.环境搭建1.Docker安装RabbitMQ1.拉取镜像2.安装命令3.开启5672和15672端口4.登录控制台 2.整合Spring AMQP1.sun-common模块下创建新模块2.引入amqp依赖和fastjson 3.新建一个mq-demo的模块1.在sun-frame下创建mq-demo2.然后在mq-demo下创建生产者和消费者子模块3.查…

CMSeasy;大米CMS漏洞复现

一、越权漏洞 pikachu-Over permission 水平越权 ⽔平越权&#xff1a;指攻击者尝试访问与他拥有相同权限的⽤户资源。 登录lucy 查看lucy个人信息 在lucy页面修改usernamelili 可以跳转lili的个人信息页面 pikachu-Over permission 垂直越权 垂直越权&#xff1a;通过低权…

【HarmonyOS之旅】ArkTS语法(一)

目录 1 -> 基本UI描述 1.1 -> 基本概念 1.2 -> UI描述规范 1.2.1 -> 无参数构造配置 1.2.2 -> 必选参数构造配置 1.2.3 -> 属性配置 1.2.4 -> 事件配置 1.2.5 -> 子组件配置 2 -> 状态管理 2.1 -> 基本概念 2.2 -> 页面级变量的状…

论文解读 | NeurIPS'24 Lambda:学习匹配先验以处理无标记垂悬问题场景下的实体对齐任务...

点击蓝字 关注我们 AI TIME欢迎每一位AI爱好者的加入&#xff01; 点击 阅读原文 观看作者讲解回放&#xff01; 作者简介 尹航&#xff0c;上海交通大学博士生 内容简介 我们研究了带有无标记悬挂问题的实体对齐&#xff08;EA&#xff09;任务&#xff0c;即部分实体在另一个…

Midjourney技术浅析(五):图像细节处理

Midjourney 作核心目标之一是生成高质量、高分辨率且细节丰富的图像。为了实现这一目标&#xff0c;Midjourney 采用了超分辨率&#xff08;Super-Resolution&#xff09;和细节增强&#xff08;Detail Enhancement&#xff09;技术。本文将深入探讨 Midjourney 的超分辨率与细…

留学生交流互动系统|Java|SSM|VUE| 前后端分离

【技术栈】 1⃣️&#xff1a;架构: B/S、MVC 2⃣️&#xff1a;系统环境&#xff1a;Windowsh/Mac 3⃣️&#xff1a;开发环境&#xff1a;IDEA、JDK1.8、Maven、Mysql5.7 4⃣️&#xff1a;技术栈&#xff1a;Java、Mysql、SSM、Mybatis-Plus、VUE、jquery,html 5⃣️数据库可…

C++第五六单元测试

1【单选题】在公有派生类的成员函数不能直接访问基类中继承来的某个成员&#xff0c;则该成员一定是基类中的&#xff08; C &#xff09;。&#xff08;2.0分&#xff09; A、公有成员B、保护成员C、私有成员D、保护成员或私有成员 注意从类外访问与从派生类中访问 2【单…

vscode实用插件(持续更新)

目录 Git History Diff Git Graph Error Lens Git History Diff 用于将当前分支的某个文件夹与远程分支的相同文件夹做对比&#xff0c;方便代码评审&#xff01;解决了为了一个问题而多次commit&#xff0c;导致代码不好评审&#xff0c;即不晓得和远程分支相比&#xff0…

MySQL第二弹----CRUD

笔上得来终觉浅,绝知此事要躬行 &#x1f525; 个人主页&#xff1a;星云爱编程 &#x1f525; 所属专栏&#xff1a;MySQL &#x1f337;追光的人&#xff0c;终会万丈光芒 &#x1f389;欢迎大家点赞&#x1f44d;评论&#x1f4dd;收藏⭐文章 ​ 一、修改表 使用ALTER …

Java中以某字符串开头且忽略大小写字母如何实现【正则表达式(Regex)】

第一种思路是先将它们都转换为小写或大写&#xff0c;再使用String类的startsWith()方法实现: 例如&#xff0c;如下的二个示例&#xff1a; "Session".toLowerCase().startsWith("sEsSi".toLowerCase()); //例子之一//例子之二String str "Hello Wo…

WPF 绘制过顶点的圆滑曲线 (样条,贝塞尔)

在一个WPF项目中要用到样条曲线&#xff0c;必须过顶点&#xff0c;圆滑后还不能太走样&#xff0c;捣鼓一番&#xff0c;发现里面颇有玄机&#xff0c;于是把我多方抄来改造的方法发出来&#xff0c;方便新手&#xff1a; 如上图&#xff0c;看代码吧&#xff1a; ----------…

SpringCloudAlibaba实战入门之路由网关Gateway断言(十二)

上一节课中我们初步讲解了网关的基本概念、基本功能,并且带大家实战体验了一下网关的初步效果,这节课我们继续学习关于网关的一些更高级有用功能,比如本篇文章的断言。 一、网关主要组成部分 上图中是核心的流程图,最主要的就是Route、Predicates 和 Filters 作用于特定路…

【Linux进程】进程信号(信号的保存与处理)

目录 前言 1. 信号的默认行为 2. 信号的保存 信号集操作函数 sigprocmask sigpending 3. 信号的处理 信号的处理过程 思考 4. sigaction 5. SIGCHLD信号 6. 可重入函数 7. volatile 总结 前言 上文介绍了信号&#xff0c;以及信号的产生&#xff0c;本文继续来聊一…

论文阅读 - 《Large Language Models Are Zero-Shot Time Series Forecasters》

Abstract 通过将时间序列编码为数字组成的字符串&#xff0c;我们可以将时间序列预测当做文本中下一个 token预测的框架。通过开发这种方法&#xff0c;我们发现像GPT-3和LLaMA-2这样的大语言模型在下游任务上可以有零样本时间序列外推能力上持平或者超过专门设计的时间序列训…

Llama系列关键知识总结

系列文章目录 第一章&#xff1a;LoRA微调系列笔记 第二章&#xff1a;Llama系列关键知识总结 文章目录 系列文章目录Llama: Open and Efficient Foundation Language Models关键要点LLaMa模型架构&#xff1a;Llama2分组查询注意力 (GQA) Llama3关键信息 引用&#xff1a; Ll…

项目实践-贪吃蛇小游戏

目录 声明 1、前言 2、实现目标 3、技术要点 4、Win32API介绍 4.1、Win32API 4.2、控制台程序 4.3、控制台屏幕上的坐标COORD 4.4、GetStdHandle 4.5、GetConsoleCursorInfo 4.6、SetConsoleCursorInfo 4.7、SetConsoleCursorPosition 4.8、GetAsyncKeyState 5、…

Java编程题_面向对象和常用API01_B级

Java编程题_面向对象和常用API01_B级 第1题 面向对象、异常、集合、IO 题干: 请编写程序&#xff0c;完成键盘录入学生信息&#xff0c;并计算总分将学生信息与总分一同写入文本文件 需求&#xff1a;键盘录入3个学生信息(姓名,语文成绩,数学成绩) 求出每个学生的总分 ,并…

Jmeter自学【8】- 使用JMeter模拟设备通过MQTT发送数据

今天使用jmeter推送数据到MQTT&#xff0c;给大家分享一下操作流程。 一、安装JMeter 参考文档&#xff1a;Jmeter自学【1】- Jmeter安装、配置 二、安装MQTT插件 1、下载插件 我的Jmeter版本是5.6.3&#xff0c;用到的插件是&#xff1a;mqtt-xmeter-2.0.2-jar-with-depe…

Uniapp跨域请求

1.什么是跨域 是指当一个请求的URL的协议、域名或端口与当前页面的URL不同时&#xff0c;该请求被视为跨域请求。跨域是一种安全策略&#xff0c;用于限制一个域的网页如何与另一个域的资源进行交互。就比如我们进行前端向后端进行发送请求的时候&#xff0c;如果是开发前后端…

基于Resnet、LSTM、Shufflenet及CNN网络的Daily_and_Sports_Activities数据集仿真

在深度学习领域&#xff0c;不同的网络结构设计用于解决特定的问题。本文将详细分析四种主流网络结构&#xff1a;卷积神经网络&#xff08;CNN&#xff09;、残差网络&#xff08;ResNet&#xff09;、长短期记忆网络&#xff08;LSTM&#xff09;和洗牌网络&#xff08;Shuff…