一般对于一些需要记录用户行为的系统,在进行网络请求时都会要求传递登录token。但是为了接口数据的安全,服务端的token一般不会设置的太长。根据需要一般为1-7天。令牌过期后,您需要重新登录。但是,频繁登录会造成不好的体验。因此,如果需要良好的体验,需要定时刷新token,更换之前的token。token的无感刷新主要有三种解决方案:方案一:后端返回过期时间,前端每次请求都会判断token的过期时间,快到过期时间就调用刷新令牌接口。缺点:后端需要额外提供token过期时间字段;本地时间用于判断,如果本地时间被篡改,尤其是本地时间比服务器时间慢时,会导致拦截失败。方法二写一个定时器,然后定时刷新token界面。缺点:浪费资源,消耗性能,不推荐。方法三在请求响应拦截器中拦截,判断token返回过期后,调用刷新token接口。结合以上三种方法,第三种是最好的,因为它不需要额外的资源。接下来我们看一下使用axios进行网络请求,然后响应service.interceptors.response的拦截。从'axios'服务导入axios.interceptors.response.use(response=>{if(response.data.code===409){returnrefreshToken({refreshToken:localStorage.getItem('refreshToken'),token:getToken()}).then(res=>{const{token}=res.datasetToken(token)response.headers.Authorization=`${token}`}).catch(err=>{removeToken()router.push('/login')returnPromise.reject(err)})}returnresponse&&response.data},(error)=>{Message.error(error.response.data.msg)returnPromise.reject(error)})问题一:如何防止token被多次刷新为了防止token被多次刷新,可以使用一个变量isRefreshing来控制token是否正在刷新。从'axios'service.interceptors.response.use(response=>{if(response.data.code===409){if(!isRefreshing){isRefreshing=truereturnrefreshToken({refreshToken:localStorage.getItem('refreshToken'),token:getToken()}).then(res=>{const{token}=res.datasetToken(token)response.headers.Authorization=`${token}`}).catch(err=>{removeToken()router.push('/login')returnPromise.reject(err)}).finally(()=>{isRefreshing=false})}}returnresponse&&response.data},(错误)=>{Message.error(error.response.data.msg)returnPromise.reject(error)})二、当同时发起两个或多个请求时,如何刷新token当第二个过期请求进来时,token刷新,我们先把这个请求存到一个数组队列中,想办法让这个请求一直等待,一直等到token刷新完毕,然后再尝试一个一个清空请求队列。那么如何让这个请求挂起呢?为了解决这个问题,我们不得不使用Promise。请求入队列后,同时返回一个Promise,使Promise一直处于Pending状态(即不调用resolve)。这时候请求就会一直等啊等啊。只要我们不执行resolve,请求就会一直在队列中。等待。当刷新请求接口返回时,我们再次调用resolve,逐一重试。importaxiosfrom'axios'//标记是否正在刷新letisRefreshing=false//重试队列letrequests=[]service.interceptors.response.use(response=>{//协议码409token过期if(response.data.code===409){if(!isRefreshing){isRefreshing=true//调用刷新令牌接口returnrefreshToken({refreshToken:localStorage.getItem('refreshToken'),token:getToken()}).then(res=>{const{token}=res.data//替换tokensetToken(token)response.headers.Authorization=`${token}`//刷新token后重新执行数组的方法requests。forEach((cb)=>cb(token))requests=[]//重新请求后清除returnservice(response.config)}).catch(err=>{//跳转到登录页面removeToken()router.push('/login')returnPromise.reject(err)}).finally(()=>{isRefreshing=false})}else{//返回没有解析的PromisereturnnewPromise(resolve=>{//以函数的形式存储resolve,等待刷新后再执行requests.push(token=>{response.headers.Authorization=`${token}`resolve(service(response.配置))})})}}返回响应&&response.data},(error)=>{Message.error(error.response.data.msg)returnPromise.reject(error)})
