在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 Tokenif (config.url.indexOf('/refreshToken') >= 0) {config.headers['token'] = RefreshToken;} else if (!(config.url.indexOf('/login') !== -1 && config.method === 'post')) {// 其他接口,请求头使用 Access Tokenconfig.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) {// 新tokenlet newToken = result.data// 保存新的accessTokenlocalStorage.setItem('accessToken', newToken)// 替换新accessTokenresponse.config.headers.token = newToken// 继续未完成的请求const resp = await service.request(response.config)// 返回请求结果return resp} else {// 清除tokenlocalStorage.clear()// 跳转到登录页router.replace('/login')}}return res
}, error => {// 返回错误信息return Promise.reject(error)
})
4、防止重复请求refreshToken接口

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

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

  • 这里使用isRefreshing变量,存放是否正在请求
// 变量isRefreshing
let isRefreshing = falseservice.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) {// 新tokenlet newToken = result.data// 保存新的accessTokenlocalStorage.setItem('accessToken', newToken)// 替换新accessTokenresponse.config.headers.token = newToken// 继续未完成的请求const resp = await service.request(response.config)// 重置状态isRefreshing = false// 返回请求结果return resp} else {// 清除tokenlocalStorage.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) {// 新tokenlet newToken = result.data// 保存新的accessTokenlocalStorage.setItem('accessToken', newToken)// 替换新accessTokenresponse.config.headers.token = newToken// token 刷新后将数组里的请求队列方法重新执行requestList.forEach((cb) => cb(newToken))// 重新请求完清空requestList = []// 继续未完成的请求const resp = await service.request(response.config)// 重置状态isRefreshing = false// 返回请求结果return resp} else {// 清除tokenlocalStorage.clear()// 重置状态isRefreshing = false// 跳转到登录页router.replace('/login')}} else {// 后面的请求走这里排队// 返回未执行 resolve 的 Promisereturn new Promise(resolve => {// 用函数形式将 resolve 存入,等待获取新token后再执行requestList.push(newToken => {response.config.headers.token = newTokenresolve(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正常的结果。
在这里插入图片描述

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

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

相关文章

机器人顶刊IJRR近期国人新作(2024)

一、IJRR简介 The International Journal of Robotics Research(IJRR)是机器人领域的高水平学术期刊,专注于发布关于机器人技术和相关领域的最新研究成果。IJRR创刊于1982年,是该领域的第一本学术刊物,2022-2023最新影…

销量王者!三一新能源搅拌车助力商砼运输走向低碳未来

2023年,在国家扩大内需和“双碳战略”的双重推进下,新老基建项目开工速度纷纷加快,各个行业对应用于工程基建、房地产等中短途混凝土运输的新能源搅拌车有了更大的需求量。 终端上牌数据显示,2023年1-12月新能源重卡累计销售23560辆,同比增长35.65%。其中,搅拌车销量暴涨,实销…

Kafka面经

1.Kafka如何保证消息不丢失 生产者: 1.Producer 默认是异步发送消息,这种情况下要确保消息发送成功,有两个方法 a. 把异步发送改成同步发送,这样 producer 就能实时知道消息发送的结果。 b. 添加异步回调函数来监听消息发送的结…

C++模拟揭秘刘谦魔术,领略数学的魅力

新的一年又开始了,大家新年好呀~。在这我想问大家一个问题,有没有同学看了联欢晚会上刘谦的魔术呢? 这个节目还挺有意思的,它最出彩的不是魔术本身,而是小尼老师“念错咒语”而导致他手里的排没有拼在一起,…

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

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

前端食堂技术周刊第 114 期:Interop 2024、TS 5.4 RC、2 月登陆浏览器的新功能、JSR、AI SDK 3.0

美味值:🌟🌟🌟🌟🌟 口味:凉拌鸡架 食堂技术周刊仓库地址:https://github.com/Geekhyt/weekly 大家好,我是童欧巴。欢迎来到前端食堂技术周刊,我们先来看下…

一本书讲透ChatGPT——理论与实践的完美结合,大模型技术工程师的必备指南

写在前面 OpenAI 在 2022 年 11 月推出了人工智能聊天应用—ChatGPT。它具有广泛的应用场景,在多项专业和学术基准测试中表现出的智力水平,不仅接近甚至有时超越了人类的平均水平。这使得 ChatGPT 在推出之初就受到广大用户的欢迎,被科技界誉…

Centos 9 安装 k8s

为了尽可能契合生产环境的部署情况,这里用kubeadm安装集群,同时方便跟随笔记一步步实践的过程,也更加了解k8s的一些特性和基础知识。 先决条件 这里将通过虚拟机安装3台centos stream 9服务器,并组成kubeneters集群(…

蓝桥杯练习题——dp

五部曲(代码随想录) 1.确定 dp 数组以及下标含义 2.确定递推公式 3.确定 dp 数组初始化 4.确定遍历顺序 5.debug 入门题 1.斐波那契数 思路 1.f[i]:第 i 个数的值 2.f[i] f[i - 1] f[i - 2] 3.f[0] 0, f[1] 1 4.顺序遍历 5.记得特判 …

SAP HANA中PAL算法使用入门

1 应用场合 SAP HANA作为一款内存数据库产品, 使得数据常驻内存, 物理磁盘的存储作为数据备份与日志记录, 以防断电内存中数据丢失. 这种构架大大的缩短了数据存取的时间, 使得SAP HANA很”高速”. 在传统数据模型中,数据库只是作为存取数据一个工具,对于类似下图所示的应用, 客…

5分钟速成渐变色css

色彩的分支——渐变色定义:按照一定规律做阶段性变化的色彩(抽象!!!) 我们可以将图片分为两块 以中心线为参考,再来看渐变色的定义:按照一定规律做阶段性变化的色彩 既然是按一定的…

京津冀光伏展

京津冀光伏展是中国在京津冀地区举办的一项光伏产业展览活动。该展览旨在展示京津冀地区光伏产业的最新发展成果,促进光伏行业的交流与合作,推动光伏产业的可持续发展。 光伏产业是指利用太阳能将光能转化为电能的产业。作为一种清洁能源,光伏…

Databend 开源周报第 134 期

Databend 是一款现代云数仓。专为弹性和高效设计,为您的大规模分析需求保驾护航。自由且开源。即刻体验云服务:https://app.databend.cn 。 Whats On In Databend 探索 Databend 本周新进展,遇到更贴近你心意的 Databend 。 支持多语句事务…

1907_Arm Cortex-M3的基本了解

1907_Arm Cortex-M3的基本了解 全部学习汇总: g_arm_cores: ARM内核的学习笔记 (gitee.com) 我发现Arm Coretex-M3有一个专门的DataSheet,看起来这个的确是被当做了一个设计的产品来对待的。正好,基于这个文件来看看M3具备哪些基本的特性&…

灯塔:HTML笔记

网页由哪些部分组成? *文字 图片 音频 视频 超链接 程序员写的代码是通过浏览器转换成网页的 五大浏览器有哪些? *IE浏览器 *火狐浏览器(Firefox) *谷歌浏览器(Chrome) *Safari浏览器 *欧朋浏览器&…

【机器学习】生成对抗网络GAN

概述 生成对抗网络(Generative Adversarial Network,GAN)是一种深度学习模型架构,由生成器(Generator)和判别器(Discriminator)两部分组成,旨在通过对抗训练的方式生成逼…

【Linux】Shell命令运行原理和权限详解

【Linux】Shell命令运行原理和权限详解 一、剩余指令的补充1.tar指令2.bc指令3.uname4.热键 二、Shell命令运行原理1.Shell2.为什么Linux不让用户直接使用kernel 三、Linux权限概念四、Linux权限管理1.文件访问的用户分类2.文件类型和访问权限(1)文件类型…

GitHub登不上:修改hosts文件来解决(GitHub520,window)

参考链接:GitHub520: 本项目无需安装任何程序,通过修改本地 hosts 文件,试图解决: GitHub 访问速度慢的问题 GitHub 项目中的图片显示不出的问题 花 5 分钟时间,让你"爱"上 GitHub。 (gitee.com) GitHub网站…

Prometheus结合Grafana监控MySQL,这篇不可不读!

📢📢📢📣📣📣 哈喽!大家好,我是【IT邦德】,江湖人称jeames007,10余年DBA及大数据工作经验 一位上进心十足的【大数据领域博主】!😜&am…

IOS 发布遇到“Unable to authenticate with App Store Connect”错误咋解决?

问题: 在开发ios app后,先发布adhoc版本,测试通过后,再发布testflight版本测试,但是可能会遇到一下问题。 解决办法: 在Signing &Capabilities中,在ios下边要指定有发布权限的Team账号&a…