一、目标
设置客户端时间,只要超过客户端时间,系统自动退回到登录页面;当未超过客户端设置的时间时,调用接口,发现令牌过期了,先调用更新令牌接口,然后再重新调用接口。
我原来的token刷新方式是:登录获取过期时间,在每次调用接口的时候比较当前时间和过期时间,如果调用接口的时候发现即将过期,刷新令牌获取新的时间;如果调用接口发现过期了,退出页面。由于是有相似之处的,所以我在此基础上进行修改。
二、思路
1、在外置参数中设置客户端时间
2、登录的时候存储客户端时间,每次调用接口的时候判断有没有过期
3、登录的时候获取token令牌
4、每次调用接口,后端会进行判断,如果过期,会报401,并在response headers中携带token-expired参数
5、如果获取到这个参数,则进行令牌刷新,并在刷新以后再重新调用过期接口
三、实现
1、在config.js 中设置客户端时间,此处设置7天过期
window.g = {
EXPIRE_TIME: 7,//过期时间 7天
}
2、在登录的时候存客户端过期时间
//登录的时候设置客户端过期时间
let expires = Date.parse(new Date())/1000 + window.g.EXPIRE_TIME *24*3600 //换算成秒
setExpires(expires)
3、每次调用接口的时候对客户端时间的判断
以get为例:
async get(url, data, query) {
//get请求方式
await expiresCalculation()
.then(() => {
//console.log("时间戳没过期");
})
.catch(() => {
//console.log("更新令牌~");
});
const options = getOptions(url, "get", data, query);
return axios(options)
.then(response => {
return checkStatus(response);
})
.catch(res => {
return checkCode(res);
});
},
import router from '@/router'
import store from '@/store'
import { getExpires } from '@/utils/auth'
const expiresCalculation = () => {
const expires = getExpires() || 0;
const nDate = new Date();
const nTime = nDate.getTime();
const nTimeSecond = Math.floor(nTime / 1000);
const expTime = expires - nTimeSecond;
return new Promise(async (resolve, reject) => {
//客户端时间过期(7天)就退出界面
if (expTime && expTime > 0) {
resolve()
} else {
await store.dispatch('user/logout')
router.push('/login');
}
})
}
export default expiresCalculation;
4、调用接口,如果报401,并在response headers中携带token-expired参数,调用令牌刷新接口,获取新token存储,并重新调接口
import {handleRefreshToken} from "@/api/http.js"
fetchData() {
getExpertPages().then(async res => {
if (!res) return
if(res && res.code === 10000){
this.patList = res.content.list
this.totalCount = res.content.totalCount
}else if(res.headers['token-expired']){ //token过期
let resBack = await handleRefreshToken(res) //刷新token并重新调用
this.patList = resBack.content.list
this.totalCount = resBack.content.totalCount
}
}).catch(() => {
...
})
}
以get为例:
http.js:
async get(url, data, query) {
//get请求方式
await expiresCalculation()
.then(() => {
//console.log("时间戳没过期");
})
.catch(() => {
//console.log("更新令牌~");
});
const options = getOptions(url, "get", data, query);
return axios(options)
.then(response => {
return checkStatus(response);
})
.catch(res => {
return checkCode(res);
});
},
function checkStatus(response) {
//处理响应数据
if (response && response.status === 200) {
...
}else if(response.status === 401){
return response;
} else{
...
}
export function handleRefreshToken(response){
//如果能走到这一步,说明客户端时间已经判断过,再判断下token
if(response.headers['token-expired']){
let data = {
accessToken: getToken(), //访问令牌
refreshToken: getRefreshToken()//刷新令牌
}
return new Promise((resolve, reject) => {
refreshToken(data).then(async (res) => {
if (res) {
const cont = res.content;
await store.dispatch('user/setNewTokenInfo', cont)
//重新调用接口
resolve(await repeatRequest(response))
}else{
console.log('token刷新失败')
reject()
}
}).catch(error =>{
reject(error)
})
})
} else {
console.log('客户端时间未过期,token过期且无token-expired')
return false
}
}
function repeatRequest(response){
let dataUrl = `${response.config.baseURL}${response.config.url}`;
// 二进制流转换为base64
return axios[response.config.method](dataUrl, {
params: response.config.params,
}).then(res => {
return checkStatus(res)
}).catch(ex => {
console.error(ex)
});
}
四、测试
以上就算实现的代码思路了。
那要怎么测试呢?登录获取正常的令牌,然后用过期的令牌调用接口,然后刷新令牌以后,需要用新的令牌再调用一次接口。所以,我这边考虑在本地storage中加一个changToken的参数做判断,登录以后changToken存入true。如果需要测试,delete这个changeToken参数。删除以后,调用任意接口都会使用一个过期的令牌。刷新令牌重新调用接口以后,参数重新变成true。
即:删除changeToken参数 =〉调用过期token =〉 调用过期token,changeToken参数变true =〉 changeToken参数变true,调用新token。
加入代码:
登录的时候:
window.localStorage.setItem('changeToken', true)
重新调用接口的时候:
function repeatRequest(response){
window.localStorage.setItem('changeToken', true)
}
请求拦截的时候:
axios.interceptors.request.use(
config => {
// 请求拦截
const isToken = (config.headers || {}).isToken === false;
let token = store.getters.token
? store.getters.token
: getToken()
? getToken()
: "";
if (token && !isToken) {
//用changeToken判断token调用
if(window.localStorage.getItem('changeToken')) {
config.headers["Authorization"] = "Bearer " + token
}
else
config.headers["Authorization"] = '此处是过期token'
config.headers["sysappid"] = sysAppId;
}
return config;
},
error => {
return Promise.reject(error);
}
);
最终实现效果: