1200字范文,内容丰富有趣,写作的好帮手!
1200字范文 > axios使用异步方式无感刷新token 简单 太简单了

axios使用异步方式无感刷新token 简单 太简单了

时间:2024-04-01 21:20:12

相关推荐

axios使用异步方式无感刷新token 简单 太简单了

文章目录

🍉 废话在前🍗 接着踩坑🥩 解决思路🍓 完整代码

🍉 废话在前

写vue的伙伴们无感刷新token相信大家都不陌生了吧,刚好,最近自己的一个项目中就需要用到这个需求,因为之前没有弄过这个,研究了一个上午,终于还是把它拿下了,小小的一个token刷新😏。

下面接着分析一下踩到的坑以及解决思路

🍗 接着踩坑

我按照之前传统的方式在返回拦截器里面进行token刷新,正常的数据可以返回,但是这个时候会有比较麻烦的地方,就是请求的数据可以在拦截器里面得到,但是不能渲染到界面上(看到这里的时候我是懵的)。

看一下代码

service.interceptors.response.use(response => {const res = response.data//刷新token的时候,可以从这里拦截到新数据,但是没有显示在页面上console.log('拦截数据:',res)if (loadingInstance) {loadingInstance.close();NProgress.done();}switch (res.code) {case 200:return rescase 401:router.push({path: "/login",})localStorage.clear();Notification.error({title: '令牌过期',message: '当前身份信息超过三天已失效,请您重新登录',duration: 0});break;default:Message({message: res.message || '请求错误',type: 'error',duration: 5 * 1000})break;}},error => {switch (error.response.status) {case 401:MessageBox.confirm('身份认证已过期,是否刷新本页继续浏览?', '提示', {confirmButtonText: '继续浏览',cancelButtonText: '退出登录',type: 'warning'}).then(() => {axios.post('/api/token/refresh/', {refresh: localStorage.getItem("retoken")}).then(response => {let res = response.data || {}if (res.code == 200) {let token = res.data.result;localStorage.setItem("token", token.access);localStorage.setItem("retoken", token.refresh);error.response.config.baseURL = '';error.response.config.headers['Authorization'] = 'Bearer ' + localStorage.getItem("token")window.location.reload();} else {router.push({path: "/login",})localStorage.clear();Notification.error({title: '令牌过期',message: '当前身份信息超过三天已失效,请您重新登录',duration: 0});}}).catch(err => {router.push({path: "/login",})localStorage.clear();Notification.error({title: '认证失败',message: '信息认证失败,请重新登录',duration: 0});})}).catch(() => {router.push({path: "/login",})Message({message: '已退出',type: 'success',})localStorage.clear();});break;case 404:Notification.error({title: '404错误',message: '服务器请求错误,请联系管理员或稍后重试。错误状态码:404',});breakdefault:Notification.error({title: '请求错误',message: '服务器请求错误,请联系管理员或稍后重试',});break;}if (loadingInstance) {loadingInstance.close();NProgress.done();}return Promise.reject(error);})

懵归懵,好在已经发现这个问题了,剩下的怎么解决呢?

当时想的是和后端配合,让后端直接发一个token过期的时间戳给我,我直接把这个时间戳放到localStrage里面,通过这个localStrage,直接在前端进行判断token的过期时间进行请求拦截,如果当前请求的时间大于了这个localStrage里面的时间,就说明token过期了,我这边就需要重新请求token了,而不要后端去进行token的验证。

信心满满的弄了一下,发现行不通,token过期的时候,后端直接一个错误401,前端就又回到解放前了。而且用时间戳的方式很容易出现bug,且在前端进行token时长的验证,很容易出现问题。因此,我还是觉得再研究一下上面那一段代码。

当然踩到坑不只是这个,还有百度和chatGPT,真的,看了一下没有找到一个可行的,总结一下主要有以下几种

状态管理vuex路由router时间戳(和我刚刚那种方式差不多)依赖注入inject刷新界面(我最开始那种方式,但是刷新的时候会出现页面白屏,且用户如果在页面上有一些自己输入的数据也会被清空,用户体验感不好)

以上方式我先不管行不行,但是麻烦是肯定的,做前端讲究的就一个字:“懒”

不是,应该是 “高效快捷”

所以这些方式就pass掉了

只能想想为什么拦截器里面可以得到数据,为什么页面位置得不到数据了

🥩 解决思路

下面是我的解决思路,有什么不对的地方还请看到的大神指出来一下😁

当用户发起请求的时候,因为刷新token的http状态码是401,这个时候axios的响应拦截器就直接进行错误捕获了,到了这里,因为数据已经返回了,但是因为是错误数据,页面得到的这个数据不可用且当前请求已经结束了,当然这里对状态码401是进行处理了的(应该获取token了)。

采用普通的获取方式来获取token,因为异步的原因,我们获取token的同时页面也在做刷新,token获取的同时,界面也刷新完毕了(但是是没有数据的,不做错误捕获会报错),因此我们在获取token完毕,且用新的token去获取数据时,拦截器里面会有数据,但是界面已经休息了,就不会把拦截器里面的新数据刷新到页面了。

因此这个地方需要对获取token的过程进行一下请求阻塞,把获取token的请求变成同步的。到这里就差不多了。直接把响应拦截器里面的error函数变成同步不就行了吗,async + await可以出来了。

以上是自己当时的想法,简单说来就是页面刷新需要慢我获取token一步,通过这个方式也确实做到了无感刷新🤣

希望以上的能帮助到你,有什么好的思路也欢迎评论区指出。

🍓 完整代码

这个是用了2.13.2版本的element-ui以及nprogress的一个axios代码模块,包含了一个下载文件的模块。

如果需要的话可以根据自己的需求来进行修改

import axios from 'axios'import {Message,Loading,Notification,} from 'element-ui'import NProgress from 'nprogress' // progress barimport 'nprogress/nprogress.css' // progress bar styleimport router from '@/router/index'const baseURL = '/api'const service = axios.create({baseURL,timeout: 6000})NProgress.configure({showSpinner: false}) // NProgress Configurationlet loadingInstance = undefined;service.interceptors.request.use(config => {NProgress.start()loadingInstance = Loading.service({lock: true,text: '正在加载,请稍候...',spinner: 'el-icon-loading',background: 'rgba(0, 0, 0, 0.3)'})config.headers['Authorization'] = 'Bearer ' + localStorage.getItem("token")return config;},error => {return Promise.reject(error)})service.interceptors.response.use(response => {const res = response.dataif (loadingInstance) {loadingInstance.close();NProgress.done();}switch (res.code) {case 200:return rescase 401:router.push({path: "/login",})localStorage.clear();Notification.error({title: '认证失效',message: '当前身份信息超过三天已失效,请您重新登录',duration: 0});break;default:Message({message: res.message || '请求错误',type: 'error',duration: 5 * 1000})break;}},async error => {switch (error.response.status) {case 401:const err401Data = error.response.data || {}if (err401Data.code !== "token_not_valid") {router.push({path: "/login",})localStorage.clear();Notification.error({title: '认证失败',message: '身份信息认证失败,请您重新登录',duration: 0});return}try {const res = await service.post('/token/refresh/', {refresh: localStorage.getItem("retoken")})if (res.code == 200) {let token = res.data.result;localStorage.setItem("token", token.access);localStorage.setItem("retoken", token.refresh);error.response.config.baseURL = ''error.response.config.headers['Authorization'] = 'Bearer ' + localStorage.getItem("token")return service(error.response.config)} else {router.push({path: "/login",})localStorage.clear();Notification.error({title: '认证失败',message: '当前身份认证信息已失效,请您重新登录',duration: 0});}} catch (error) {router.push({path: "/login",})localStorage.clear();Notification.error({title: '认证失败',message: '身份认证失败,请您重新登录,失败原因:' + error.message,duration: 0});}breakcase 404:Notification.error({title: '404错误',message: '服务器请求错误,请联系管理员或稍后重试。错误状态码:404',});breakdefault:Notification.error({title: '请求错误',message: '服务器请求错误,请联系管理员或稍后重试',});break;}if (loadingInstance) {loadingInstance.close();NProgress.done();}return Promise.reject(error);})// 文件下载通用方式export const requestFile = axios.create({baseURL,timeout: 0, //关闭超时时间});requestFile.interceptors.request.use((config) => {config.headers['Authorization'] = 'Bearer ' + localStorage.getItem("token") //携带的请求头config.responseType = 'blob';loadingInstance = Loading.service({lock: true,text: '正在下载,请稍候...',spinner: 'el-icon-loading',background: 'rgba(0, 0, 0, 0.3)'})return config;});requestFile.interceptors.response.use((response) => {let res = response.data;if (loadingInstance) {loadingInstance.close()}// const contentType = response.headers['content-type'];//获取返回的数据类型let blob = new Blob([res], {type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" //文件下载以及上传类型为 .xlsx});let url = window.URL.createObjectURL(blob);// 创建一个链接元素let link = document.createElement('a');link.href = url;link.download = '产品列表.xlsx'; // 自定义文件名link.click();},(err) => {Message({message: '操作失败,请联系管理员',type: 'error',})if (loadingInstance) {loadingInstance.close()}});export default service;

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。
相关阅读
无感刷新token方法

无感刷新token方法

2024-02-04

前端无感刷新token

前端无感刷新token

2022-11-11

vue无感刷新token

vue无感刷新token

2020-03-17

实现双token无感刷新

实现双token无感刷新

2022-06-12