令牌過期以後重新整理token並重調介面
一、目標
設定客戶端時間,只要超過客戶端時間,系統自動退回到登入頁面;當未超過客戶端設定的時間時,呼叫介面,發現令牌過期了,先呼叫更新令牌介面,然後再重新呼叫介面。
我原來的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);
}
);
最終實現效果: