因为服务器的token一版不会设置太长,token过期后就需要重新登录,频繁的登录会造成体验不好的问题,因此,需要体验好的话,就需要定时去刷新token,并替换之前的token。以下是token失效的效果:
那么做到token的无感刷新,主要有3种方案:
方案一:
后端返回过期时间,前端每次请求就判断token的过期时间,快到过期时间,就去调用刷新token接口。
缺点:若本地时间被篡改,特别是本地时间比服务器时间慢时,拦截会失败,不建议采用。
方案二:
后端返回过期时间,前端写个定时器,然后定时刷新token接口。
缺点:浪费资源,消耗性能,不建议采用。
方案三:
在请求响应拦截器中拦截,判断token 返回过期后,调用刷新token接口,推荐使用。
那么以下是方案三的实现步骤:
1、首次登录的时候会获取到两个token,把获取到的两个token存到前端缓存中。
-
localStorage.
setItem(
'refreshToken',xxx)
-
localStorage.
setItem(
'token', xxx)
2、token刷新基础写法
-
import axios
from
"axios"
-
-
let flag =
false
// 设置开关,防止多次请求
-
let expiredList = []
//存储过期的请求
-
-
// 把过期请求添加在数组中
-
function
addRequest(
request) {
-
expiredList.
push(request)
-
}
-
-
-
// 创建axios实例
-
const service = axios.
create({
-
baseURL:
'http://xxxxxxxxx',
//后台请求接口路径
-
timeout:
10000
// 请求超时时间
-
})
-
-
service.
interceptors.
request.
use(
-
config => {
-
localStorage.
getItem(
'token') && (config.
headers.
Authorization =
localStorage.
getItem(
'token'))
-
config.
headers[
'Content-Type'] = config.
headers[
'Content-Type'] ||
'application/json;charset=utf-8'
-
return config
-
},
-
error => {
-
Promise.
reject(error)
-
}
-
)
-
-
service.
interceptors.
response.
use(
res => {
-
const code = res.
data.
code
-
if (code ===
70001 || code ===
401) {
//与后端约定相应的code以此判断是否过期
-
// 把过期请求存储起来,用于请求到新的刷新token
-
addRequest(
() =>
resolve(
http(config)))
-
// 用刷新token去请求新的主token
-
if (!flag) {
-
flag =
true;
-
// 获取刷新token
-
let refreshToken =
localStorage.
getItem(
'refreshToken');
-
if (refreshToken) {
-
// 判断刷新token是否过期, getRefreshToken这个是你封装的请求刷新token的接口
-
getRefreshToken(refreshToken).
then(
res => {
-
if (判断刷新token失效,退出登录) {
-
flag =
false
-
// 移除刷新token
-
localStorage.
removeItem(
'refreshToken')
-
localStorage.
removeItem(
'token')
-
location.
href =
'/'
//跳转到你指定的页面
-
}
else
if (res.
code ===
1000) {
-
flag =
false
-
// 重新发送请求
-
expiredList.
forEach(
(request) =>
request())
-
expiredList = []
-
}
-
})
-
}
-
}
-
}
else
if(code ===
200){
-
return res.
data
-
}
-
},
-
error => {
-
return
Promise.
reject(error)
-
}
-
)
-
-
export
default service
3、token刷新进阶写法(包含防止多次刷新token、同时发起两个或者两个以上的请求时刷新token)
-
import axios
from
'axios'
-
-
// 是否正在刷新的标记
-
let isRefreshing =
false
-
-
//存储过期的请求
-
let expiredList = []
-
-
// 创建axios实例
-
const service = axios.
create({
-
baseURL:
'http://xxxxxxxxx',
//后台请求接口路径
-
timeout:
10000
// 请求超时时间
-
})
-
-
service.
interceptors.
request.
use(
-
config => {
-
localStorage.
getItem(
'token') && (config.
headers.
Authorization =
localStorage.
getItem(
'token'))
-
config.
headers[
'Content-Type'] = config.
headers[
'Content-Type'] ||
'application/json;charset=utf-8'
-
return config
-
},
-
error => {
-
Promise.
reject(error)
-
}
-
)
-
-
service.
interceptors.
response.
use(
-
res => {
-
//约定code 401 token 过期
-
if (res.
data.
code ===
401) {
-
if (!isRefreshing) {
-
isRefreshing =
true
-
//调用刷新token的接口
-
return
refreshToken({
refreshToken:
localStorage.
getItem(
'refreshToken'),
token:
localStorage.
getItem(
'token') }).
then(
res => {
-
const { token } = res.
data
-
// 替换token
-
localStorage.
setItem(
'token', token)
-
res.
headers.
Authorization =
`${token}`
-
// token 刷新后将数组的方法重新执行
-
expiredList.
forEach(
(cb) =>
cb(token))
-
expiredList = []
// 重新请求完清空
-
return
service(res.
config)
-
}).
catch(
err => {
-
localStorage.
removeItem(
'refreshToken')
-
localStorage.
removeItem(
'token')
-
location.
href =
'/'
//跳转到你指定的页面
-
return
Promise.
reject(err)
-
}).
finally(
() => {
-
isRefreshing =
false
-
})
-
}
else {
-
// 返回未执行 resolve 的 Promise
-
return
new
Promise(
resolve => {
-
// 用函数形式将 resolve 存入,等待刷新后再执行
-
expiredList.
push(
token => {
-
res.
headers.
Authorization =
`${token}`
-
resolve(
service(res.
config))
-
})
-
})
-
}
-
}
-
return res && res.
data
-
},
-
(error) => {
-
return
Promise.
reject(error)
-
}
-
)
4、上述写法是方便在此文章中理解无感刷新token的代码,其中储存token到本地缓存中的代码可以提取出来,然后在使用到的地方导入使用即可。
-
例子:把需要的方法提取出来
-
-
export
function
getRefreshTokenn(
) {
-
return
localStorage.
getItem(refreshToken)
-
}
-
-
export
function
setRefreshToken(
token) {
-
return
localStorage.
setItem(
'refreshToken',token)
-
}
-
-
export
function
removeRefreshToken(
) {
-
return
localStorage.
removeItem(
'refreshToken')
-
}
-
-
使用方法:
-
import { getRefreshTokenn, setRefreshToken, removeRefreshToken }
from
'@/utils/auth'
-
-
把
localStorage.
removeItem(
'refreshToken') 这段代码替换成
removeRefreshToken()