Project background

Recently, I made a web project of oa class. The user needs to log in to access the page normally. After the user successfully logs in, the back-end interface returns a token to the front-end. Each subsequent interface invocation carries the token, and the server verifies the token to determine whether the user has successfully logged in. Of course, the token is time-dependent. When the token expires, we can log in again to obtain a new token, but the experience of doing so will be poor, so we are required to use the old token to exchange for the new token. That is to say, we need to call an interface to refresh the token and transfer the previous token to the server to exchange for the latest token. After obtaining the latest token, we need to call the interface to return the status due to the expiration of the token again, so that the user can refresh the token without perception.

Plan to discuss

Use of axios axios. Interceptors. Response. Use () interface request intercept, when token expired, we get the latest token by calling the refresh token method, carry the latest token for a request again. Generally, the display data of a page needs to call multiple interfaces. If the token expires, each interface will call the refresh token once, which is bound to cause a waste of resources. What we hope is that if the token expires, we only call the refresh token interface once. My solution here is to define let isRefreshing = false; Tag variable that marks whether the token status is being refreshed. If the token status is being refreshed, the interface for refreshing the token will not be called. Need to be solved, there is also a difficult point is more interfaces a request at the same time, assuming that the first interface into the first, and then refresh token interface, because of the reason of tag variables, other interface will not refresh token, we need to put the rest of the interface into a queue, when after the refresh token interface has been completed, We use Promise to place all unexecuted resolve elements in an array. Each element in the array is in a pending state. When the refresh request interfaces return, we call resolve and release them one by one.

implementation

Here I create a localstorage. js file to encapsulate the localStorage of js. We will store the token obtained from the server in the browser cache. The code is as follows:

export default {
	// Local storage encapsulation
	get(key){
		let data=localStorage.getItem(key);
		return JSON.parse(data)
	},
	set(key,val){
		let value=JSON.stringify(val);
		localStorage.setItem(key,value); }}Copy the code

Create a service.js file that encapsulates the implementation of AXIos and the refresh token as follows:

import axios from "axios"; / / introduce axios
import qs from "qs";// Serialize the POST interface request parameters
import LocalStorage from "./localStorage.js"; //H5 local storage encapsulation method
let SECURITY_URL = "http://192.168.1.17:5505/security-amass/";// Refresh the Base path of the token interface
axios.defaults.headers.post["Content-Type"] ="application/x-www-form-urlencoded; charset=UTF-8";
axios.defaults.headers.post["Access-Control-Allow-Origin"] = "*";
axios.defaults.withCredentials = false; / / carry cookies
// Create an axios instance
const instance = axios.create({
  timeout: 300000});// Whether the token interface is being requested to refresh
let isRefreshing = false;
// Request queue
let requests = [];
// Refresh the token method
function refreshToken(params) {
  return instance.get(SECURITY_URL + "oauth/token", params);
}
// Request result interceptor
instance.interceptors.response.use(
  (response) = > {
    // This is where the token expires
    const config = response.config;
    let code = response.data.code;
    // The code value here is agreed with the backend, 40009 means that the token has expired
    if (code == "40009") {
      if(! isRefreshing) { isRefreshing =true;
        let refresh_token = LocalStorage.get("refresh_token");
        // Here are the parameters to be passed to refresh the token in my project, which parameters need to be confirmed with the back-end development
        let loginData = {
          grant_type: "refresh_token".client_id: "impawning".client_secret: "impawning",
          refresh_token,
        };
        refreshToken({ params: loginData })
          .then((res) = > {
            let access_token = res.data.access_token;
            let refresh_token = res.data.refresh_token;
            LocalStorage.set("access_token", res.data.access_token);
            LocalStorage.set("refresh_token", res.data.refresh_token);
            config.headers["access_token"] = access_token;
            config.headers["refresh_token"] = refresh_token;
            requests.forEach((cb) = > cb(access_token, refresh_token));
            requests = [];
            return instance(config);
          })
          .catch((err) = > {
            window.location.href = "/";
          })
          .finally(() = > {
            isRefreshing = false;
          });
      } else {
        // Refreshing tokens returns an unperformed resolve promise
        return new Promise((resolve) = > {
          // Queue resolve, save it as a function, and execute it directly after token refresh
          requests.push((access_token, refresh_token) = > {
            config.headers["access_token"] = access_token;
            config.headers["refresh_token"] = refresh_token; resolve(instance(config)); }); }); }}else if (code == "40005" || code == "40003") {// Tokens are invalid and incorrect
      window.location.href = "/";
    } else {
      returnresponse; }},(error) = > {
    return Promise.reject(error); });const api = {
  get(url, data) {
    instance.defaults.headers.common["access_token"] = LocalStorage.get(
      "access_token"
    );
    instance.defaults.headers.common["refresh_token"] = LocalStorage.get(
      "refresh_token"
    );
    return instance.get(url, { params: data });
  },
  post(url, data) {
    instance.defaults.headers.common["access_token"] = LocalStorage.get(
      "access_token"
    );
    instance.defaults.headers.common["refresh_token"] = LocalStorage.get(
      "refresh_token"
    );
    // Post interface encapsulation
    returninstance.post(url, qs.stringify(data)); }};export { api };
Copy the code

The above is an example of using AXIos to implement no perception refresh token. Please point out any questions or errors in the article. Thank you for reading.