通常,对于一些须要记录用户行为的零碎,在进行网络申请的时候都会要求传递一下登录的token。不过,为了接口数据的平安,服务器的token个别不会设置太长,依据须要个别是1-7天的样子,token过期后就须要从新登录。不过,频繁的登录会造成体验不好的问题,因而,须要体验好的话,就须要定时去刷新token,并替换之前的token。
要做到token的无感刷新,次要有3种计划:
计划一:
后端返回过期工夫,前端每次申请就判断token的过期工夫,如果快到过期工夫,就去调用刷新token接口。
毛病:须要后端额定提供一个token过期工夫的字段;应用了本地工夫判断,若本地工夫被篡改,特地是本地工夫比服务器工夫慢时,拦挡会失败。
办法二
写个定时器,而后定时刷新token接口。
毛病:浪费资源,耗费性能,不倡议采纳。
办法三
在申请响应拦截器中拦挡,判断token 返回过期后,调用刷新token接口。
综合下面的三个办法,最好的是第三个,因为它不须要占用额定的资源。接下来,咱们看一下应用axios进行网络申请,而后响应service.interceptors.response的拦挡。
import axios from 'axios'service.interceptors.response.use( response => { if (response.data.code === 409) { return refreshToken({ refreshToken: localStorage.getItem('refreshToken'), token: getToken() }).then(res => { const { token } = res.data setToken(token) response.headers.Authorization = `${token}` }).catch(err => { removeToken() router.push('/login') return Promise.reject(err) }) } return response && response.data }, (error) => { Message.error(error.response.data.msg) return Promise.reject(error) })
问题一:如何避免屡次刷新token
为了避免屡次刷新token,能够通过一个变量isRefreshing 去管制是否在刷新token的状态。
import axios from 'axios'service.interceptors.response.use( response => { if (response.data.code === 409) { if (!isRefreshing) { isRefreshing = true return refreshToken({ refreshToken: localStorage.getItem('refreshToken'), token: getToken() }).then(res => { const { token } = res.data setToken(token) response.headers.Authorization = `${token}` }).catch(err => { removeToken() router.push('/login') return Promise.reject(err) }).finally(() => { isRefreshing = false }) } } return response && response.data }, (error) => { Message.error(error.response.data.msg) return Promise.reject(error) })
二、同时发动两个或者两个以上的申请时,怎么刷新token
当第二个过期的申请进来,token正在刷新,咱们先将这个申请存到一个数组队列中,想方法让这个申请处于期待中,始终等到刷新token后再一一重试清空申请队列。
那么如何做到让这个申请处于期待中呢?为了解决这个问题,咱们得借助Promise。将申请存进队列中后,同时返回一个Promise,让这个Promise始终处于Pending状态(即不调用resolve),此时这个申请就会始终等啊等,只有咱们不执行resolve,这个申请就会始终在期待。当刷新申请的接口返回来后,咱们再调用resolve,一一重试。
import axios from 'axios'// 是否正在刷新的标记let isRefreshing = false//重试队列let requests = []service.interceptors.response.use( response => { //约定code 409 token 过期 if (response.data.code === 409) { if (!isRefreshing) { isRefreshing = true //调用刷新token的接口 return refreshToken({ refreshToken: localStorage.getItem('refreshToken'), token: getToken() }).then(res => { const { token } = res.data // 替换token setToken(token) response.headers.Authorization = `${token}` // token 刷新后将数组的办法从新执行 requests.forEach((cb) => cb(token)) requests = [] // 从新申请完清空 return service(response.config) }).catch(err => { //跳到登录页 removeToken() router.push('/login') return Promise.reject(err) }).finally(() => { isRefreshing = false }) } else { // 返回未执行 resolve 的 Promise return new Promise(resolve => { // 用函数模式将 resolve 存入,期待刷新后再执行 requests.push(token => { response.headers.Authorization = `${token}` resolve(service(response.config)) }) }) } } return response && response.data }, (error) => { Message.error(error.response.data.msg) return Promise.reject(error) })