所用框架
vue+axios
为什么要刷新token
假设后端设置的token过期时间为10分钟。那么登录以后,过十分钟后token就会过期,这时再去操作系统,所有的请求都不能用,都会报token过期,需要重新登录才能继续操作系统。这样的方式显然是不合理的,为了解决这个问题,就需要在一段时间内刷新token。
实现思路
在请求拦截器里实现token刷新逻辑处理。后端设置的token过期时间为30分钟,我们这里是在token即将过期的时候进行token刷新,而不是已经过期了才去刷新,这里即将过期的时间设置的是10分钟(也就是token使用20分钟后),这个时间在下面代码判断中可以根据自己项目情况自行调整。在当前时间大于设置的即将过期时间时,调用刷新token的接口,重新设置token,并将即将过期时间进行调整。设置即将过期时间是为了避免每个请求都去刷新token,避免频繁修改token。
一般一个页面同时会有很多个请求,所以我们需要建一个数组先缓存起来这些请求;除此之外,还要建一个全局标志,绑在window上,用来判断当前是否正在刷新token;待token刷新完成后,再去执行数组中缓存的所有请求,此时,数组中的请求都是带着刷新后的最新的token值作为headers去请求的。
下面直接上相关代码
login.js中,登录后设置即将过期时间:
import{setTokenOverdueTime}from'../src/api/refreshToken'
localStorage.setItem("tokenOverdueTime",setTokenOverdueTime());
新建一个refreshToken.js文件,文件内容如下:
import Vue from'vue'//设置token快过期时间,当前时间+20分钟exportfunctionsetTokenOverdueTime(){let t =newDate().getTime()+1200000;let d =newDate(t);let theMonth = d.getMonth()+1;let theDate = d.getDate();let theHours = d.getHours();let theMinutes = d.getMinutes();if(theMonth <10){
theMonth ='0'+ theMonth
}if(theDate <10){
theDate ='0'+ theDate
}if(theHours <10){
theHours ='0'+ theHours
}if(theMinutes <10){
theMinutes ='0'+ theMinutes
}let date = d.getFullYear()+'-'+ theMonth +'-'+ theDate
let time = theHours +':'+ theMinutes
let Spare = date +' '+ time
return Spare;}// 判断token是否即将过期functionisTokenExpired(){let curTime =newDate();//获取即将过期时间let tokenOverdueTime = localStorage.getItem("tokenOverdueTime");// 判断当前时间是否大于即将过期时间if(curTime >newDate(tokenOverdueTime)){returntrue}returnfalse;}// 将所有的请求都push到数组中,其实数组是[function(token){}, function(token){},...]functioncacheRequestArrHandle(cb){
cacheRequestArr.push(cb);}// 数组中的请求得到新的token之后自执行,用新的token去重新发起请求functionafreshRequest(token){
cacheRequestArr.map(cb=>cb(token));
cacheRequestArr =[];}//定义一个空数组,用来缓存请求let cacheRequestArr =[];// 是否正在刷新的标志
window.isRefreshing =false;exportfunctionisRefreshToken(instance, config, store){// 判断token是否即将过期,且不是请求刷新token的接口if(isTokenExpired()&& config.url !=='/refreshToken'){// 所有的请求来了,先判断是否正在刷新token,// 如果不是,将刷新token标志置为true并请求刷新token.// 如果是,则先将请求缓存到数组中// 等到刷新完token后再次重新请求之前缓存的请求接口即可if(!window.isRefreshing){
window.isRefreshing =true;
instance.get('/refreshToken').then(res=>{if(res.data.status ===0){// 更新 store和缓存里的值
localStorage.setItem("userToken",JSON.stringify(res.data.data));
store.dispatch("setUserToken", res.data.data);// 更新即将过期时间
localStorage.setItem("tokenOverdueTime",setTokenOverdueTime());// 将刷新的token替代老的token
config.headers.token = res.data.data;// 刷新token完成后重新请求之前的请求afreshRequest(res.data.data);}}).finally(()=>{
window.isRefreshing =false;})// 下面这段代码一定要写,不然第一个请求的接口带过去的token还是原来的,要将第一个请求也缓存起来let retry =newPromise((resolve)=>{cacheRequestArrHandle((token)=>{
config.headers.token = token;// token为刷新完成后传入的token// 将请求挂起resolve(config)})})return retry;}else{let retry =newPromise((resolve)=>{cacheRequestArrHandle((token)=>{
config.headers.token = token;// token为刷新完成后传入的token// 将请求挂起resolve(config)})})return retry;}}else{return config
}}
设置拦截器:
import store from'../../store';import{isRefreshToken}from'../src/api/refreshToken'/**
* 请求拦截
* interceptors
* @param instance
* */exportconstinterceptors=(instance)=>{//请求拦截
instance.interceptors.request.use(config=>{// 请求头携带token 和envlet token = localStorage.getItem("userToken")?JSON.parse(localStorage.getItem("userToken")):null;if(token){
config.headers.token = token;}let flag =isRefreshToken(instance, config, store);return flag ? flag : config;},(error)=>{return Promise.reject(error);})//响应拦截
instance.interceptors.response.use(res=>{//返回数据if(res.data.dev)return res;if(res.data.code !==0){return Promise.reject(res);}else{return res;}},(error)=>{return Promise.reject(error);})}
token过期后退出登录的这边没有代码,可以自己加上。
如果觉得对你有帮助~可以点个赞,鼓励鼓励作者哟!
版权归原作者 前端备忘录 所有, 如有侵权,请联系我们删除。