The front end painlessly refreshes the Token
This requirement scenario is very common, and it is used in almost any project. It has been implemented in previous projects, and it just happened that a project was implemented recently.
demand
For the need for the front-end to achieve painless refresh Token, there are no more than two:
- Check whether the Token expires before the request. If the Token expires, refresh the Token
- After the request is received, check whether the request expires according to the returned status. If the request expires, refresh
Processing logic
The implementation is not much different, but the position of judgment is different, the core principle is the same:
- judge
Token
Is late- If not expired, normal processing
- Expiration triggers a refresh
Token
The request of- Get a new
Token
save - To resend
Token
Expired Indicates the request initiated during this period
- Get a new
Key points:
- keep
Token
Expired time to initiate a request status (cannot enter a failed callback) - The refresh
Token
After resending the request, the response data is returned to the corresponding caller
implementation
- Create a Flag
isRefreshing
To determine whether it is refreshing - Create an array queue
retryRequests
To save requests that need to be reinitiated - Judgment to
Token
overdueisRefreshing = false
In case a refresh is initiatedToken
The request of- The refresh
Token
After traversing the execution queueretryRequests
- The refresh
isRefreshing = true
Indicates refreshingToken
, returns aPending
The state of thePromise
And saves the request information to the queueretryRequests
In the
import axios from "axios";
import Store from "@/store";
import Router from "@/router";
import { Message } from "element-ui";
import UserUtil from "@/utils/user";
// Create an instance
const Instance = axios.create();
Instance.defaults.baseURL = "/api";
Instance.defaults.headers.post["Content-Type"] = "application/json";
Instance.defaults.headers.post["Accept"] = "application/json";
// Define a flag to determine whether to refresh the Token
let isRefreshing = false;
// Save the queue that needs to be reinitiated
let retryRequests = [];
// Request interception
Instance.interceptors.request.use(async function(config) {
Store.commit("startLoading");
const userInfo = UserUtil.getLocalInfo();
if (userInfo) {
// Business needs to place Token information in params, usually in headers
config.params = Object.assign(config.params ? config.params : {}, {
appkey: userInfo.AppKey,
token: userInfo.Token
});
}
return config;
});
// Response interception
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
) {
// The Token status needs to be refreshed. 30001 40001 42001 40014
// Get the configuration for this request
let config = response.config;
// The login page does not refresh the Token
if(Router.currentRoute.path ! = ="/login") {
if(! isRefreshing) {// Changing the flag state indicates that the Token is being refreshed
isRefreshing = true;
/ / refresh Token
return Store.dispatch("user/relogin")
.then(res= > {
// Set the refreshed Token
config.params.token = res.Token;
config.params.appkey = res.AppKey;
// Iterate over the queue where the request needs to be reinitiated
retryRequests.forEach(cb= > cb(res));
// Clear the queue
retryRequests = [];
return Instance.request(config);
})
.catch(() = > {
retryRequests = [];
Message.error("Automatic login failed. Please log in again.");
const code = Store.state.user.info.CustomerCode || "";
// Failed to refresh the Token Clear the cached user information and adjust it to the login page
Store.dispatch("user/logout");
Router.replace({
path: "/login".query: { redirect: Router.currentRoute.fullPath, code: code }
});
})
.finally(() = > {
// Reset the flag when the request is complete
isRefreshing = false;
});
} else {
// Refreshing tokens returns an unperformed resolve promise
// Save the promise's resolve to the queue's callback, waiting to be invoked after the Token is refreshed
// The original caller will wait until the queue reinitiates the request, and then return the response, so that the user is unaware of the purpose (painless refresh)
return new Promise(resolve= > {
// Queue resolve, save it as a function, and execute it directly after token refresh
retryRequests.push(info= > {
// Reassign the new Tokenconfig.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;
Copy the code
The writing is not very good, forgive me