在vue前端开发中基于refreshToken和axios拦截器实现token的无感刷新

文章目录

      • 一、需求背景
      • 二、token刷新的方案
        • 1、根据过期时间重新获取
        • 2、定时刷新token接口
        • 3、使用了RefreshToken
      • 三、关于RefreshToken
      • 四、Refresh Token的优点
      • 五、Refresh Token的工作原理
      • 六、Refresh Token的使用流程
      • 七、Refresh Token的实现步骤
        • 1、登录成功后保存AccessToken,RefreshToken
        • 2、正常请求业务接口的时候携带AccessToken
        • 3、响应拦截器处理401权限错误
        • 4、防止重复请求refreshToken接口
        • 5、同时多个请求返回401,需要刷新token
      • 八、总结
      • 九、代码上传
        • 1、vue项目部分
        • 2、nodejs服务部分
      • 十、效果展示

一、需求背景

对于一些需要记录用户行为的系统,在进行网络请求的时候都会要求传递一下登录的token。不过,为了接口数据的安全,服务器的token一般不会设置太长,根据需要一般是30分钟的样子,token过期后就需要重新登录。不过,频繁的登录会造成体验不好的问题,因此,需要体验好的话,就需要定时去刷新token,并替换之前的token。

实现token无感刷新对于前端来说是一项十分常用的技术,其本质都是为了优化用户体验,当token过期时不需要用户调回登录页重新登录,而是当token失效时,进行拦截,发送刷新token的请求,获取最新的token进行覆盖,让用户感受不到token已过期。

二、token刷新的方案

1、根据过期时间重新获取

后端返回过期时间,前端判断token过期时间,去调用刷新token的接口。

缺点:需要后端额外提供一个token过期时间的字段;使用了本地时间判断,若本地时间被篡改,特别是本地时间比服务器时间慢时,拦截会失败。

2、定时刷新token接口

根据token过期时间,写一个定时器,定时刷新token接口

缺点:浪费资源,消耗性能,不建议采用。

3、使用了RefreshToken

后端在登录之后会给前端一个RefreshToken字段同AccessToken一并传过来。token失效后利用RefreshToken去延长用户的登录信息。

三、关于RefreshToken

RefreshToken 方法是现代 Web 应用中一种常见的身份验证机制,尤其在需要长时间保持用户登录状态的场景下具有重要意义。

RefreshToken 方法的主要作用是在用户登录后,服务器生成一个 RefreshToken 并将其返回给客户端。客户端在之后的每次请求中都需要携带这个 RefreshToken,以便服务器能够验证用户身份并返回用户所需的数据。

使用场景包括但不限于:用户在应用中的长时间操作、用户在多个设备上使用应用、用户需要跨域访问应用等。在这些场景下,RefreshToken 方法能够有效地减少用户重复登录的次数,提高用户体验。

四、Refresh Token的优点

  • 安全性增强:Refresh Token不同于AccessToken,它只在第一次获取和刷新时在网络中传输,因此被盗的风险远小于AccessToken。同时,Refresh Token是加密字符串,并且和token是相关联的,相比获取各种资源的token,refresh token的作用仅仅是获取新的token,因此其作用和安全性要求都大为降低。

  • 减少服务器负担:使用Refresh Token刷新服务端不需要刷新Token的过期时间,一旦Token过期,就反馈给前端,前端使用Refresh Token申请一个全新Token继续使用。这种方案中,服务端只需要在客户端请求更新Token的时候对Refresh Token的有效性进行一次检查,大大减少了更新有效期的操作,也就避免了频繁读写。

  • 提高用户体验:由于Refresh Token的存在,用户在访问令牌过期后不需要重新登录,提高了用户体验。

五、Refresh Token的工作原理

  • 当AccessToken过期时,客户端使用Refresh Token发起刷新请求。
  • 认证服务器验证Refresh Token的有效性。
  • 如果Refresh Token有效,认证服务器会生成一个新的AccessToken,并返回给客户端。
  • 客户端收到新的AccessToken后,可以继续使用该token访问受保护资源。

在这里插入图片描述

六、Refresh Token的使用流程

  • 首次登录的时候会获取到两个token(AccessToken,RefreshToken)
  • 持久化保存起来(localStorage方案)
  • 正常请求业务接口的时候携带AccessToken
  • 当接口口返回401权限错误时,使用RefreshToken请求接口获取新的AccessToken
  • 替换原有旧的AccessToken,并保存
  • 继续未完成的请求,携带AccessToken
  • RefreshToken也过期了,跳转回登录页面,重新登录

七、Refresh Token的实现步骤

1、登录成功后保存AccessToken,RefreshToken

登录请求登录接口authorization(),这里省略了。
比如我们请求登录接口"authorization"成功后,后端返回我们2个字段。

data:{
    code:200,
    msg:'ok',
    accessToken:'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6ImFkbWluIiwiaWF0IjoxMzIyNTc1MDcyOSwiZXhwIjoxNzA2NjMwMzk5fQ.sTLeqLl9lgG4OW40RNXdoZz9NO2bgCOOtnXuErRkXBM',
    RefreshToken:'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6ImFkbWluIiwiaWF0IjoxMzIyNTc1MDcyOSwiZXhwIjoxNzA2NjMzMzk5fQ.GF-j_rFEMNwh7H7o4MbM5EFspFC5lQ1zxD85e70nOiM',
}

保存到localStorage里面

localStorage.setItem('accessToken', res.data.data.accessToken);
localStorage.setItem('RefreshToken', res.data.data.RefreshToken);
2、正常请求业务接口的时候携带AccessToken
import axios from 'axios'

// 创建axios实例
const service = axios.create({
  timeout: 20000, // 请求超时时间(毫秒)
})

// 请求拦截器
service.interceptors.request.use((config) => {
	const accessToken = localStorage.getItem('accessToken');
	const RefreshToken = localStorage.getItem('RefreshToken');
	if (config.url) {
		// 此处为 Refresh Token 专用接口,请求头使用 Refresh Token
		if (config.url.indexOf('/refreshToken') >= 0) {
			config.headers['token'] = RefreshToken;
		} else if (!(config.url.indexOf('/login') !== -1 && config.method === 'post')) {
			// 其他接口,请求头使用 Access Token
			config.headers['token'] = accessToken;
		}
		return config;
	}
}, error => {
	return Promise.reject(error);
})
3、响应拦截器处理401权限错误
service.interceptors.response.use(async (response) => {
	let res = response.data
	// 为了演示,这里仅展示处理状态码为401的情况
	if (res.code == '401') {
			// 这里是获取新token的接口,方法在这里省略了。
			const result = await refreshToken()
			// 获取成功
			if (result && result.data) {
				// 新token
				let newToken = result.data
				// 保存新的accessToken
				localStorage.setItem('accessToken', newToken)
				// 替换新accessToken
				response.config.headers.token = newToken
				// 继续未完成的请求
				const resp = await service.request(response.config)
				// 返回请求结果
				return resp
			} else {
				// 清除token
				localStorage.clear()
				// 跳转到登录页
				router.replace('/login')
			}
	}
	return res
}, error => {
	// 返回错误信息
	return Promise.reject(error)
})
4、防止重复请求refreshToken接口

为了防止多次刷新token,可以通过一个变量isRefreshing 去控制是否正在请求刷新token的接口。

响应拦截器处理,防止同时多次调用刷新token接口。

  • 这里使用isRefreshing变量,存放是否正在请求
// 变量isRefreshing
let isRefreshing = false

service.interceptors.response.use(async (response) => {
	let res = response.data
	// 为了演示,这里仅展示处理状态码为401的情况
	if (res.code == '401') {
		// 控制是否在刷新token的状态
		if (!isRefreshing) {
			// 修改isRefreshing状态
			isRefreshing = true
			// 这里是获取新token的接口,方法在这里省略了。
			const result = await refreshToken()
			// 获取成功
			if (result && result.data) {
				// 新token
				let newToken = result.data
				// 保存新的accessToken
				localStorage.setItem('accessToken', newToken)
				// 替换新accessToken
				response.config.headers.token = newToken

				// 继续未完成的请求
				const resp = await service.request(response.config)
				// 重置状态
				isRefreshing = false
				// 返回请求结果
				return resp
			} else {
				// 清除token
				localStorage.clear()
				// 重置状态
				isRefreshing = false
				// 跳转到登录页
				router.replace('/login')
			}
		} 
	}
	return res
}, error => {
	// 返回错误信息
	return Promise.reject(error)
})
5、同时多个请求返回401,需要刷新token

第一个refreshToken接口还没返回,后面的请求又过来了,防止同时多次调用刷新token接口,先把后面这些请求放在一个数组里面,等到refreshToken接口成功后,我们再逐个重试数组里面的请求。

响应拦截器处理,同时多个请求返回401,需要刷新token

  • 这是使用了requestList存放请求队列
// 变量isRefreshing
let isRefreshing = false
// 后续的请求队列
let requestList = []

service.interceptors.response.use(async (response) => {
	let res = response.data
	// 为了演示,这里仅展示处理状态码为401的情况
	if (res.code == '401') {
		// 控制是否在刷新token的状态
		if (!isRefreshing) {
			// 修改isRefreshing状态
			isRefreshing = true
			// 这里是获取新token的接口,方法在这里省略了。
			const result = await refreshToken()
			// 获取成功
			if (result && result.data) {
				// 新token
				let newToken = result.data
				// 保存新的accessToken
				localStorage.setItem('accessToken', newToken)
				// 替换新accessToken
				response.config.headers.token = newToken

				// token 刷新后将数组里的请求队列方法重新执行
				requestList.forEach((cb) => cb(newToken))
				// 重新请求完清空
				requestList = []

				// 继续未完成的请求
				const resp = await service.request(response.config)
				// 重置状态
				isRefreshing = false
				// 返回请求结果
				return resp
			} else {
				// 清除token
				localStorage.clear()
				// 重置状态
				isRefreshing = false
				// 跳转到登录页
				router.replace('/login')
			}
		} else {
			// 后面的请求走这里排队
			// 返回未执行 resolve 的 Promise
			return new Promise(resolve => {
				// 用函数形式将 resolve 存入,等待获取新token后再执行
				requestList.push(newToken => {
					response.config.headers.token = newToken
					resolve(service(response.config))
				})
			})
		}
	}
	return res
}, error => {
	// 返回错误信息
	return Promise.reject(error)
})

八、总结

基本的思路是这样的,你也可以根据自己的业务需要,自己修改。

  • 比如抽离上面的方法或逻辑,单独封装。

  • 你也可以添加接口失败重连的逻辑。

  • 你也可以使用数据加密传输,例如sm4等。

九、代码上传

这里我做了个简单的demo演示,可以到顶部下载。

1、vue项目部分

下载依赖

npm i

启动项目

npm run serve

启动后项目地址为:http://localhost:8080

1、先进入登录页面,点击’登录’按钮,请求’login’接口,接口返回accessToken、RefreshToken。

2、跳转到首页,正常携带token请求’getTableList’接口,接口返回列表数据。

3、下面做了3个按钮来测试接口返回401的状态。

  • 点击1个按钮,用来测试’test1’接口返回401状态,响应拦截器做了处理自动请求’refreshToken’。

  • 连续点击2个或3个按钮,用来测试防止重复请求refreshToken接口。第一个refreshToken接口还没返回,后面的请求又过来了,防止同时多次调用刷新token接口,先把后面这些请求放在一个数组里面,等到refreshToken接口成功后,我们再逐个重试数组里面的请求。

2、nodejs服务部分

做了个简单的nodejs服务,里面写了对应的测试接口。

下载依赖

npm i

启动服务

npm run serve

启动后服务为:http://localhost:3000

  • 为了演示接口401状态,test1、test2、test3接口第1次请求返回401,后面就返回200正常状态。

  • 接口getTableList为普通接口,正常返回数据。

  • 接口login、refreshToken里面的token为模拟的JWT格式的token。

  • 接口refreshToken为了演示,里面做了延迟5s返回数据。

十、效果展示

登录页面
在这里插入图片描述

首页
在这里插入图片描述

这里是同时点击3个按钮,第1个test1请求返回401后,去请求refreshToken,因为这个接口做了5s延迟,不会立即返回结果,后面test2,test3也都返回401,因为做了判断,所有不会重复去请求refreshToken。等到refreshToken返回结果后,会自动去重新请求请求队列里面的接口。后面陆续返回了test1、test2、test3正常的结果。
在这里插入图片描述

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

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

相关文章

持安科技孙维伯:零信任在攻防演练下的最佳实践|DISCConf 2023

近日,在2023数字身份安全技术大会上,持安科技联合创始人孙维伯应主办方的特别邀请,发表了主题为“零信任在攻防演练下的最佳实践”的演讲。 孙维伯在2023数字身份安全技术大会上发表演讲 以下为本次演讲实录: 我是持安科技的联合…

WPF 【十月的寒流】学习笔记(2):MVVM中是怎么实现通知的

文章目录 前言相关链接代码仓库项目配置代码初始代码ViewPersonViewModel 尝试老办法通知解决方案ObservableCollectionBindingListICollectionView 总结 前言 我们这次详细了解一下列表通知的底层是怎么实现的 相关链接 十月的寒流 MVVM实战技巧之:可被观测的集合…

深入理解Java中的优先级队列(堆)——PriorityQueue

引言: 在Java中,优先级队列(PriorityQueue)是一种基于堆结构实现的队列,其中每个元素都有一个优先级,优先级高的元素在队列中具有更高的优先级,排在前面。优先级队列常用于任务调度、事件处理等…

js 面试运行机制和存储(从以下几方面理解),栈和堆的理解

1 工作原理 每个浏览器都有自己的引擎,通过引擎把代码解析运行起来。 2 生命周期 3-1 内存分配 3-2 内存使用 3-3 内存回收 3 栈和堆的理解 timer也是个函数--所以也是引用类型。 4 如何运行 以下可忽略 首先声明变量,放在左侧栈中执行,在执行…

MATLAB环境下一种新颖的类脉冲信号的高分辨率时频分析方法

一般情况下,机械振动信号或地震信号是非平稳的。而传统傅立叶变换只能应用于平稳信号分析,故不适用于非平稳信号。所以,我们需要采用时频分析方法。时频分析方法能达到同时在时间域和频率域对信号进行分析的目的,得到信号在不同时…

makefileGDB使用

一、makefile 1、make && makefile makefile带来的好处就是——自动化编译,一旦写好,只需要一个make命令,整个工程完全自动编译,极大的提高了软件开发的效率 下面我们通过如下示例来进一步体会它们的作用: ①…

从预训练到通用智能(AGI)的观察和思考

1.预训练词向量 预训练词向量(Pre-trained Word Embeddings)是指通过无监督学习方法预先训练好的词与向量之间的映射关系。这些向量通常具有高维稠密特征,能够捕捉词语间的语义和语法相似性。最著名的预训练词向量包括Google的Word2Vec&#…

(2024,MixLoRA,任务干扰,独立因子选择,条件因子选择)使用 LoRA 的条件混合进行多模态指令调优

Multimodal Instruction Tuning with Conditional Mixture of LoRA 公和众和号:EDPJ(进 Q 交流群:922230617 或加 VX:CV_EDPJ 进 V 交流群) 目录 0. 摘要 3. 任务干扰在多模态指令调优中的 LoRA 应用 3.1 背景&am…

深度神经网络联结主义的本质

一、介绍 在新兴的人工智能 (AI) 领域,深度神经网络 (DNN) 是一项里程碑式的成就,突破了机器学习、模式识别和认知模拟的界限。这一技术奇迹的核心是一个与认知科学本身一样古老的思想:联结主义。本文深入探讨了联结主义的基本原理&#xff0…

Nodejs 第四十三章(redis)

Redis(Remote Dictionary Server)是一个开源的内存数据结构存储系统,它提供了一个高效的键值存储解决方案,并支持多种数据结构,如字符串(Strings)、哈希(Hashes)、列表&a…

React之组件定义和事件处理

一、组件的分类 在react中,组件分为函数组件和class组件,也就是无状态组件和有状态组件。 * 更过时候我们应该区别使用无状态组件,因为如果有状态组件会触发生命周期所对应的一些函数 * 一旦触发他生命周期的函数,它就会影响当前项…

X-pin扁线电机制造工艺复杂 联合电子率先实现其量产

X-pin扁线电机制造工艺复杂 联合电子率先实现其量产 扁线电机是扁平铜包线绕组的电机。扁线电机是目前主流电机绕组形式,根据技术路线不同,扁线电机分为I-pin扁线电机、Hair-pin扁线电机、X-pin扁线电机等,其中X-pin扁线电机是指采用X-pin绕组…

utniy urp shinyssrr插件使用

文章目录 前言步骤1首先在URP的配置文件里添加SSR后处理2 修改RenderingPath为延迟渲染3 启用深度纹理4 为物体添加脚本 插件下载 前言 用来实现屏幕空间反射效果 unity 版本为2021.3.8LTS,低版本的untiy URP的参数设置位置z可能会不同 步骤 1首先在URP的配置文件…

专访win战略会任志雄:澳门旅游业复苏 挖掘游客消费潜力

南方财经:各个国家地区的客商都有不同文化背景和消费习惯,应如何更好吸引外地客商来澳门? win战略会任志雄:首先,周边国家的市场潜力都非常大,包括韩国、日本、越南和印度。 这些年来,这些国家的经济增长都很高,居民的出游比重也在持续增加,如果他们国家的居民把澳门作为一个重…

初学JavaWeb开发总结

0 什么是Web开发 Web: 全球广域网,又称万维网(www World Wide Web),能够通过浏览器访问的网站。 Web开发,就是开发网站的,如:淘宝、京东等等。 1 网站的工作流程 流程: 浏览器先向前端服务器请求前端资…

一文看清楚流程自定义表单究竟好不好用

提升办公协作效率、做好数据资源利用率的话,可以用什么样的软件实现?在低代码技术平台领域奋斗多年,流辰信息服务商可以给大家推荐专用的流程自定义表单及低代码技术平台整套服务方案。如果你想知道流程自定义表单好不 好用,有什么…

idc业务具体包含哪些业务

IDC业务,即互联网数据中心业务,是指提供互联网基础设施服务的一种商业模式。它包括了许 多不同的业务,每个业务都有其特定的功能和用途。下面将详细介绍IDC业务具体包含哪些业务。 1. 服务器托管服务: 服务器托管是IDC业务中最基…

transformer--解码器

在编码器中实现了编码器的各种组件,其实解码器中使用的也是这些组件,如下图: 解码器组成部分: 由N个解码器层堆叠而成每个解码器层由三个子层连接结构组成第一个子层连接结构包括一个多头自注意力子层和规范化层以及一个残差连接第二个子层连…

<专利>机器人3D视觉快速定位抓取方法及系统

摘要,此专利无可用的关键技术信息,基本都是下面几句话反复说。。。 本发明提供了一种机器人3D快速定位抓取方法及系统, 包括: 通过高速的3D结构光成像对目标物体的表面轮廓进行扫描, 形成点云数据;对所述点…

浅谈排序算法(冒泡,插入,归并)

对于数据的排序,有多种方法,对应这不同的时间复杂度(效率不同)。 ​一、冒泡排序(Bubble Sort) 冒泡排序(Bubble Sort)是一种简单的排序算法。 算法思路: 1. 从第一对相…