js無痛刷新Token的實(shí)現(xiàn)
這個(gè)需求場(chǎng)景很常見,幾乎很多項(xiàng)目都會(huì)用上,之前項(xiàng)目也實(shí)現(xiàn)過,最近剛好有個(gè)項(xiàng)目要實(shí)現(xiàn),重新梳理一番。
需求
對(duì)于需要前端實(shí)現(xiàn)無痛刷新Token,無非就兩種:
- 請(qǐng)求前判斷Token是否過期,過期則刷新
- 請(qǐng)求后根據(jù)返回狀態(tài)判斷是否過期,過期則刷新
處理邏輯
實(shí)現(xiàn)起來也沒多大差別,只是判斷的位置不一樣,核心原理都一樣:
- 判斷
Token是否過期沒過期則正常處理
過期則發(fā)起刷新Token的請(qǐng)求拿到新的
Token保存重新發(fā)送
Token過期這段時(shí)間內(nèi)發(fā)起的請(qǐng)求
重點(diǎn):
- 保持
Token過期這段時(shí)間發(fā)起請(qǐng)求狀態(tài)(不能進(jìn)入失敗回調(diào)) - 把刷新
Token后重新發(fā)送請(qǐng)求的響應(yīng)數(shù)據(jù)返回到對(duì)應(yīng)的調(diào)用者
實(shí)現(xiàn)
- 創(chuàng)建一個(gè)flag
isRefreshing來判斷是否刷新中 - 創(chuàng)建一個(gè)數(shù)組隊(duì)列
retryRequests來保存需要重新發(fā)起的請(qǐng)求 - 判斷到
Token過期isRefreshing = false的情況下 發(fā)起刷新Token的請(qǐng)求刷新
Token后遍歷執(zhí)行隊(duì)列retryRequestsisRefreshing = true表示正在刷新Token,返回一個(gè)Pending狀態(tài)的Promise,并把請(qǐng)求信息保存到隊(duì)列retryRequests中
import axios from "axios";
import Store from "@/store";
import Router from "@/router";
import { Message } from "element-ui";
import UserUtil from "@/utils/user";
// 創(chuàng)建實(shí)例
const Instance = axios.create();
Instance.defaults.baseURL = "/api";
Instance.defaults.headers.post["Content-Type"] = "application/json";
Instance.defaults.headers.post["Accept"] = "application/json";
// 定義一個(gè)flag 判斷是否刷新Token中
let isRefreshing = false;
// 保存需要重新發(fā)起請(qǐng)求的隊(duì)列
let retryRequests = [];
// 請(qǐng)求攔截
Instance.interceptors.request.use(async function(config) {
Store.commit("startLoading");
const userInfo = UserUtil.getLocalInfo();
if (userInfo) {
//業(yè)務(wù)需要把Token信息放在 params 里面,一般來說都是放在 headers里面
config.params = Object.assign(config.params ? config.params : {}, {
appkey: userInfo.AppKey,
token: userInfo.Token
});
}
return config;
});
// 響應(yīng)攔截
Instance.interceptors.response.use(
async function(response) {
Store.commit("finishLoading");
const res = response.data;
if (res.errcode == 0) {
return Promise.resolve(res);
} else if (
res.errcode == 30001 ||
res.errcode == 40001 ||
res.errcode == 42001 ||
res.errcode == 40014
) {
// 需要刷新Token 的狀態(tài) 30001 40001 42001 40014
// 拿到本次請(qǐng)求的配置
let config = response.config;
// 進(jìn)入登錄頁(yè)面的不做刷新Token 處理
if (Router.currentRoute.path !== "/login") {
if (!isRefreshing) {
// 改變flag狀態(tài),表示正在刷新Token中
isRefreshing = true;
// 刷新Token
return Store.dispatch("user/relogin")
.then(res => {
// 設(shè)置刷新后的Token
config.params.token = res.Token;
config.params.appkey = res.AppKey;
// 遍歷執(zhí)行需要重新發(fā)起請(qǐng)求的隊(duì)列
retryRequests.forEach(cb => cb(res));
// 清空隊(duì)列
retryRequests = [];
return Instance.request(config);
})
.catch(() => {
retryRequests = [];
Message.error("自動(dòng)登錄失敗,請(qǐng)重新登錄");
const code = Store.state.user.info.CustomerCode || "";
// 刷新Token 失敗 清空緩存的用戶信息 并調(diào)整到登錄頁(yè)面
Store.dispatch("user/logout");
Router.replace({
path: "/login",
query: { redirect: Router.currentRoute.fullPath, code: code }
});
})
.finally(() => {
// 請(qǐng)求完成后重置flag
isRefreshing = false;
});
} else {
// 正在刷新token,返回一個(gè)未執(zhí)行resolve的promise
// 把promise 的resolve 保存到隊(duì)列的回調(diào)里面,等待刷新Token后調(diào)用
// 原調(diào)用者會(huì)處于等待狀態(tài)直到 隊(duì)列重新發(fā)起請(qǐng)求,再把響應(yīng)返回,以達(dá)到用戶無感知的目的(無痛刷新)
return new Promise(resolve => {
// 將resolve放進(jìn)隊(duì)列,用一個(gè)函數(shù)形式來保存,等token刷新后直接執(zhí)行
retryRequests.push(info => {
// 將新的Token重新賦值
config.params.token = info.Token;
config.params.appkey = info.AppKey;
resolve(Instance.request(config));
});
});
}
}
return new Promise(() => {});
} else {
return Promise.reject(res);
}
},
function(error) {
let err = {};
if (error.response) {
err.errcode = error.response.status;
err.errmsg = error.response.statusText;
} else {
err.errcode = -1;
err.errmsg = error.message;
}
Store.commit("finishLoading");
return Promise.reject(err);
}
);
export default Instance;到此這篇關(guān)于js無痛刷新Token的實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)js無痛刷新Token內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
給超鏈接添加特效鼠標(biāo)移動(dòng)展示提示信息且隨鼠標(biāo)移動(dòng)
需要實(shí)現(xiàn)這樣的效果,就是給超鏈接添加特效當(dāng)鼠標(biāo)移動(dòng)到上展示提示信息且提示信息跟隨鼠標(biāo)移動(dòng),經(jīng)測(cè)試還不錯(cuò),感興趣的朋友可以參考下2013-10-10
JavaScript實(shí)現(xiàn)私有屬性的幾種方式小結(jié)
在JavaScript中,私有屬性是指只能在對(duì)象內(nèi)部訪問的屬性,外部無法直接訪問,JavaScript并沒有提供官方的私有屬性的支持,但可以通過一些技巧來模擬實(shí)現(xiàn)私有屬性,所以本文給大家總結(jié)了JavaScript實(shí)現(xiàn)私有屬性的幾種方式,需要的朋友可以參考下2024-04-04
layer彈出層倒計(jì)時(shí)關(guān)閉的實(shí)現(xiàn)方法
今天小編就為大家分享一篇layer彈出層倒計(jì)時(shí)關(guān)閉的實(shí)現(xiàn)方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2019-09-09
javascript面向?qū)ο笾L問對(duì)象屬性的兩種方式分析
這篇文章主要介紹了javascript面向?qū)ο笾L問對(duì)象屬性的兩種方式分析,實(shí)例分析了直接訪問對(duì)象屬性的方式與數(shù)組訪問方式,需要的朋友可以參考下2015-01-01
JavaScript Promise原理與實(shí)現(xiàn)刨析
首先呢,Promise是異步中比較重要的知識(shí)點(diǎn),學(xué)習(xí)的最好方法就是掌握它的基本原理。所以這一篇主要說一下如何用JS來實(shí)現(xiàn)一個(gè)自己的promise2022-10-10
JS鮮為人知的問題之[] == ![]結(jié)果為true、而{} == !{}卻為false
這篇文章主要給大家介紹了關(guān)于JS鮮為人知的問題之[] == ![]結(jié)果為true、而{} == !{}卻為false的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-05-05
js實(shí)現(xiàn)文本框支持加減運(yùn)算的方法
這篇文章主要介紹了js實(shí)現(xiàn)文本框支持加減運(yùn)算的方法,可實(shí)現(xiàn)文本框輸入加減運(yùn)算式同時(shí)右側(cè)實(shí)時(shí)顯示對(duì)應(yīng)計(jì)算結(jié)果的功能,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-08-08

