Spring Cloud实战 | 最八篇:Spring Cloud +Spring Security OAuth2+ Vue前后端分离模式下无感知刷新实现JWT续期

简介: Spring Cloud实战 | 最八篇:Spring Cloud +Spring Security OAuth2+ Vue前后端分离模式下无感知刷新实现JWT续期

微信图片_20230709223728.gif


项目名称 地址

后台 youlai-mall

管理前端 youlai-mall-admin

微信小程序 youlai-mall-weapp

一. 前言

记得上一篇Spring Cloud的文章关于如何使JWT失效进行了理论结合代码实践的说明,想当然的以为那篇会是基于Spring Cloud统一认证架构系列的最终篇。但关于JWT另外还有一个热议的话题是JWT续期?。


本篇就个人觉得比较好的JWT续期方案以及落地和大家分享一下,算是抛转引玉,大家有好的方案欢迎留言哈。


后端


Spring Cloud实战 | 第一篇:Windows搭建Nacos服务

Spring Cloud实战 | 第二篇:Spring Cloud整合Nacos实现注册中心

Spring Cloud实战 | 第三篇:Spring Cloud整合Nacos实现配置中心

Spring Cloud实战 | 第四篇:Spring Cloud整合Gateway实现API网关

Spring Cloud实战 | 第五篇:Spring Cloud整合OpenFeign实现微服务之间的调用

Spring Cloud实战 | 第六篇:Spring Cloud Gateway+Spring Security OAuth2+JWT实现微服务统一认证授权

Spring Cloud实战 | 最七篇:Spring Cloud Gateway+Spring Security OAuth2集成统一认证授权平台下实现注销使JWT失效方案

Spring Cloud实战 | 最八篇:Spring Cloud +Spring Security OAuth2+ Vue前后端分离模式下无感知刷新实现JWT续期

管理前端


vue-element-admin实战 | 第一篇: 移除mock接入后台,搭建有来商城youlai-mall前后端分离管理平台

vue-element-admin实战 | 第二篇: 最小改动接入后台实现根据权限动态加载菜单

微信小程序


vue+uniapp商城实战 | 第一篇:【有来小店】微信小程序快速开发接入Spring Cloud OAuth2认证中心完成授权登录

二. 方案

理论背景: 在 有来商城 微服务项目 OAuth2实现微服务的统一认证的背景下,前端调用/oauth/token接口认证,在认证成功会返回两个令牌access_token和refresh_token,出于安全考虑access_token时效相较refresh_token短很多(access_token默认12小时,refresh_token默认30天)。当access_token过期或者将要过期时,需要拿refresh_token去刷新获取新的access_token返回给客户端,但是为了客户良好的体验需要做到无感知刷新。


方案一:


浏览器起一个定时轮询任务,每次在access_token过期之前刷新。


方案二:


请求时返回access_token过期的异常时,浏览器发出一次使用refresh_token换取access_token的请求,获取到新的access_token之后,重试因access_token过期而失败的请求。


方案比较:


第一种方案实现简单,但在access_token过期之前刷新,那些旧access_token依然能够有效访问,如果使用黑名单的方式限制这些就的access_token无疑是在浪费资源。


第二种方案是在access_token已经失效的情况下才去刷新便不会有上面的问题,但是它会多出来一次请求,而且实现起来考虑的问题相较下比较多,例如在token刷新阶段后面来的请求如何处理,等获取到新的access_token之后怎么重新重试这些请求。


总结:第一种方案实现简单;第二种方案更为严谨,过期续期不会造成已被刷掉的access_token还有效;总之两者都是可行方案,本篇就第二种方案如何通过前后端的配合实现无感知刷新token实现JWT续期展开说明。


三. 实现

直接进入主题,如何通过代码实现在access_token过期时使用refresh_token刷新续期,本篇涉及代码基于Spring Cloud后端youlai-mall 和 Vue前端 youlai-mall-admin,需要的小伙伴可以下载到本地参考下,如果对你有帮助,也希望给个star,感谢~


后端

后端部分这里唯一工作是在网关youlai-gateway鉴定access_token过期时抛出一个自定义异常提供给前端判定,如下图所示:

微信图片_20230709223748.png



小伙伴们在这里也许会有疑问,网关这里如何判断JWT是否已过期?先不急,下文会说明,先看实现之后再说原理。


前端

1. OAuth2客户端设置

设置OAuth2客户端支持刷新模式,只有这样才能使用refresh_token刷新换取新的access_token。以及为了方便我们测试分别设置access_token和refresh_token的过期时间,因为默认的12小时和30天我们吃不消的;除此之外,还必须满足t(refresh_token) > 60s + t(access_token)的条件, refresh_token的时效大于access_token时效我们可以理解,那这个60s是怎么回事,别急还是先看实现,原因下文会说明。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lTEVuJzR-1606266325797)(https://i.loli.net/2020/11/22/qBsFPIlw6Sp5CnG.png)]


2. 添加刷新令牌方法

设置了支持客户端刷新模式之后,在前端添加一个refreshToken方法,调用的接口和登录认证是同一个接口/oauth/token,只是参数授权方式grant_type的值由password切换到refresh_token,即密码模式切换到刷新模式,这个方法作用是在刷新token之后将新的token写入到localStorage覆盖旧的token。

微信图片_20230709223801.png


3. 请求响应拦截添加令牌过期处理

在判断响应结果是token过期时,执行刷新令牌方法覆盖本地的token。


在刷新期间需做到两点,一是避免重复刷新,二是请求重试,为了满足以上两点添加了两个关键变量:


refreshing----刷新标识

在第一次access_token过期请求失败时,调用刷新token请求时开启此标识,标识当前正在刷新中,避免后续请求因token失效重复刷新。


waitQueue----请求等待队列

当执行刷新token期间时,需要把后来的请求先缓存到等待队列,在刷新token成功时,重新执行等待队列的请求即可。


修改请求响应封装request.js的代码如下,关键部分使用注释说明,完整工程 youlai-mall-admin


let refreshing = false,// 正在刷新标识,避免重复刷新

 waitQueue = [] // 请求等待队列

service.interceptors.response.use(

 response => {

   const {code, msg, data} = response.data

   if (code !== '00000') {

     if (code === 'A0230') { // access_token过期 使用refresh_token刷新换取access_token

       const config = response.config

       if (refreshing == false) {

         refreshing = true

         const refreshToken = getRefreshToken()

         return store.dispatch('user/refreshToken', refreshToken).then((token) => {

           config.headers['Authorization'] = 'Bearer ' + token

           config.baseURL = '' // 请求重试时,url已包含baseURL

           waitQueue.forEach(callback => callback(token)) // 已刷新token,所有队列中的请求重试

           waitQueue = []

           return service(config)

         }).catch(() => { // refresh_token也过期,直接跳转登录页面重新登录

           MessageBox.confirm('当前页面已失效,请重新登录', '确认退出', {

             confirmButtonText: '重新登录',

             cancelButtonText: '取消',

             type: 'warning'

           }).then(() => {

             store.dispatch('user/resetToken').then(() => {

               location.reload()

             })

           })

         }).finally(() => {

           refreshing = false

         })

       } else {

         // 正在刷新token,返回未执行resolve的Promise,刷新token执行回调

         return new Promise((resolve => {

           waitQueue.push((token) => {

             config.headers['Authorization'] = 'Bearer ' + token

             config.baseURL = '' // 请求重试时,url已包含baseURL

             resolve(service(config))

           })

         }))

       }

     } else {

       Message({

         message: msg || '系统出错',

         type: 'error',

         duration: 5 * 1000

       })

     }

   }

   return {code, msg, data}

 },

 error => {

   return Promise.reject(error)

 }

)


四. 测试

完成上面前后端代码调整之后,接下来进入测试,还记得上面设置access_token时效为1s、refresh_token为120s吧。这里access_token设置为1s,但是时效确是61s,至于原因下文细说。这里把测试根据时间分为3个阶段:


0~61s:双token都没过期,正常请求过程。

微信图片_20230709223825.png


61s~120s:access_token过期,再次请求会执行一次刷新请求。

微信图片_20230709223827.png

微信图片_20230709223852.png



120s+: refresh_token过期,神仙都救不了,重新登录。

微信图片_20230709223902.png


五. 问题

声明: 问题基于youlai-mall项目使用的nimbus-jose-jwt这个JWT库,依赖spring-security-oauth2-jose这个jar包。


1. 如何判定JWT过期?


JWT的是否过期判断最终落点是在JwtTimestampValidator#validate方法上


2.为什么access_token比设定多了60s时效?


开挂?有后台?向天再借60s?


刚开始在不知情的情况下以为自己哪里配置错了,设置5s过期,等个1min多钟。后来确实没办法决心去调试下源码,最后找到JWT验证过期的方法JwtTimestampValidator#validate

微信图片_20230709223911.png



基本上满足 Instant.now(this.clock).minus(this.clockSkew).isAfter(expiry) 就说明JWT过期了


now - 60s > expiry =转换=> now > expiry + 60s


按正常理解当前时间大于过期时间就可判定为过期,但这里却在过期时间加了个时钟偏移60s,活生生的延长了一分钟,至于为什么?没找到说明文档,注释也没说明,知道的小伙伴欢迎下方留言~


六. 总结

本篇讲述 youlai-mall 项目中如何通过前后端配合利用双token刷新实现JWT续期的功能需求,后端抛出token过期异常,前端捕获之后调用刷新token请求,成功则完成续期,失败(一般指refresh_token也过期了)则需要重新登录。在代码的实现过程中了解到在资源服务器(youlai-gateway)如何判断JWT是否过期、axios如何进行请求重试等一些问题。


最后说一下自己的项目,youlai-mall 集成当前主流开发模式微服务加前后端分离,当前最新主流技术栈 Spring Cloud + Spring Cloud Alibaba + Vue , 以及最流行统一认证授权Spring Cloud Gateway + Spring Security OAuth2 + JWT。所以觉得本文对你有所帮助的话给个关注(持续更新中…),或者对该项目感兴趣的小伙伴给个star,也期待你的加入和建议,还是老样子有问题随时联系我~(微信号:haoxianrui)。


相关文章
|
人工智能 Java Serverless
【MCP教程系列】搭建基于 Spring AI 的 SSE 模式 MCP 服务并自定义部署至阿里云百炼
本文详细介绍了如何基于Spring AI搭建支持SSE模式的MCP服务,并成功集成至阿里云百炼大模型平台。通过四个步骤实现从零到Agent的构建,包括项目创建、工具开发、服务测试与部署。文章还提供了具体代码示例和操作截图,帮助读者快速上手。最终,将自定义SSE MCP服务集成到百炼平台,完成智能体应用的创建与测试。适合希望了解SSE实时交互及大模型集成的开发者参考。
15576 60
|
前端开发 Java 关系型数据库
基于Java+Springboot+Vue开发的鲜花商城管理系统源码+运行
基于Java+Springboot+Vue开发的鲜花商城管理系统(前后端分离),这是一项为大学生课程设计作业而开发的项目。该系统旨在帮助大学生学习并掌握Java编程技能,同时锻炼他们的项目设计与开发能力。通过学习基于Java的鲜花商城管理系统项目,大学生可以在实践中学习和提升自己的能力,为以后的职业发展打下坚实基础。技术学习共同进步
908 7
|
11月前
|
前端开发 安全 Java
基于springboot+vue开发的会议预约管理系统
一个完整的会议预约管理系统,包含前端用户界面、管理后台和后端API服务。 ### 后端 - **框架**: Spring Boot 2.7.18 - **数据库**: MySQL 5.6+ - **ORM**: MyBatis Plus 3.5.3.1 - **安全**: Spring Security + JWT - **Java版本**: Java 11 ### 前端 - **框架**: Vue 3.3.4 - **UI组件**: Element Plus 2.3.8 - **构建工具**: Vite 4.4.5 - **状态管理**: Pinia 2.1.6 - **HTTP客户端
1502 4
基于springboot+vue开发的会议预约管理系统
|
前端开发 JavaScript Java
基于springboot+vue开发的校园食堂评价系统【源码+sql+可运行】【50809】
本系统基于SpringBoot与Vue3开发,实现校园食堂评价功能。前台支持用户注册登录、食堂浏览、菜品查看及评价发布;后台提供食堂、菜品与评价管理模块,支持权限控制与数据维护。技术栈涵盖SpringBoot、MyBatisPlus、Vue3、ElementUI等,适配响应式布局,提供完整源码与数据库脚本,可直接运行部署。
646 6
基于springboot+vue开发的校园食堂评价系统【源码+sql+可运行】【50809】
|
11月前
|
监控 Cloud Native Java
Spring Integration 企业集成模式技术详解与实践指南
本文档全面介绍 Spring Integration 框架的核心概念、架构设计和实际应用。作为 Spring 生态系统中的企业集成解决方案,Spring Integration 基于著名的 Enterprise Integration Patterns(EIP)提供了轻量级的消息驱动架构。本文将深入探讨其消息通道、端点、过滤器、转换器等核心组件,以及如何构建可靠的企业集成解决方案。
940 0
|
供应链 JavaScript BI
ERP系统源码,基于SpringBoot+Vue+ElementUI+UniAPP开发
这是一款专为小微企业打造的 SaaS ERP 管理系统,基于 SpringBoot+Vue+ElementUI+UniAPP 技术栈开发,帮助企业轻松上云。系统覆盖进销存、采购、销售、生产、财务、品质、OA 办公及 CRM 等核心功能,业务流程清晰且操作简便。支持二次开发与商用,提供自定义界面、审批流配置及灵活报表设计,助力企业高效管理与数字化转型。
1001 2
ERP系统源码,基于SpringBoot+Vue+ElementUI+UniAPP开发
|
监控 数据可视化 JavaScript
springboot + vue的MES系统生产计划管理源码
MES系统(制造执行系统)的生产计划管理功能是其核心模块之一,涵盖生产计划制定与优化、调度排程、进度监控反馈、资源管理调配及可视化报告五大方面。系统基于SpringBoot + Vue-Element-Plus-Admin技术栈开发,支持多端应用(App、小程序、H5、后台)。通过实时数据采集与分析,MES助力企业优化生产流程,适用于现代化智能制造场景。
716 1
|
负载均衡 Dubbo Java
Spring Cloud Alibaba与Spring Cloud区别和联系?
Spring Cloud Alibaba与Spring Cloud区别和联系?
|
Java Nacos Sentinel
Spring Cloud Alibaba:一站式微服务解决方案
Spring Cloud Alibaba(简称SCA) 是一个基于 Spring Cloud 构建的开源微服务框架,专为解决分布式系统中的服务治理、配置管理、服务发现、消息总线等问题而设计。
3485 13
Spring Cloud Alibaba:一站式微服务解决方案