Mechanism and Implementation of token

  • 2021-10-13 06:39:19
  • OfStack

Preface

I was asked to refresh token in the interview before, but I am not clear about the details of token verification mechanism. After discussing the new project with the students at the back end, refresh token is used to realize it. This article mainly shares the understanding and implementation of token mechanism under 1.

How to login authentication

Login authentication 1 generally has two purposes, one for security and one for user convenience. Because HTTP is stateless, the back end can't know where the request comes from after receiving it, but most of the time we have the need to verify the user's identity, and the front end has the need to save the user's login status. However, if the user information is stored in the front end, it is very dangerous and easy to get, so there is an asymmetric encryption way to realize login verification and storage in the back end.

At present, the main login authentication methods are cookie + session, token, single sign-on and OAuth third-party login. In this article, we mainly talk about token login authentication.

What is token

token literally means token. In fact, the back end encrypts the user information asymmetrically, and then saves the encrypted content in the front end. When sending the request, it takes this token to realize authentication. The general process is that the first login user enters the user name and password, After the server verifies that it is correct, it will encrypt the user's information asymmetrically to generate a token and return it to the front end. The front end can store it in cookie or localStorage, etc. This token will be brought with each request sent in the future, and the back end will identify the user's identity and the legality of the request by verifying the token.

The advantage of token is that the server does not need to save token, but only needs to verify token transmitted from the front end, so this method can also be used for distributed deployment several times. The downside of token is that because the server does not save session status, it cannot revoke an token or change the permissions of token during use. That is, once token is signed, it will remain valid until it expires, unless the server deploys additional logic.

At present, the commonly used encryption method of token is JWT JSON Web Token. For JWT, please refer to Ruan Yifeng's introductory tutorial on JSON Web Token

token refresh

According to the above token logic, the front end only needs to save one token passed from the back end, and attach it every time. When the token expires, there are two options. We can have the user flush your login, or the back end generates a new token, and the front end saves the new token and resends the request. However, there are problems in both ways. If users are allowed to log in again, the user experience is not very good, and frequent re-login is not a better interactive way. However, if a new token is automatically generated, there will be security problems. For example, if a hacker obtains an expired token and sends a request to the backend, he can also obtain an updated token.

In order to weigh the above problems, a mechanism for refreshing token is produced. When the user logs in successfully for the first time, the backend will return two token and one accessToken for request, that is, we attach accessToken to each request, while refreshToken is used to refresh accessToken when accessToken expires. Generally speaking, accessToken will be attached every time, so the security risk is relatively high, so the expiration time is relatively short, while refreshToken will only be sent to the back end when accessToken expires, so the security risk is relatively low, so the expiration time can be 1 point longer.

When our accessToken expires, we will refresh the interface request to the back-end token and pass in refreshToken. After the back-end verifies the Meiyu problem, it will give us a new accessToken, which can guarantee the continuity of access after saving. Of course, this is not absolutely safe, but a relatively safe practice. 1 We save two token in localStorage.

Realization of refreshing token

I mainly used axios in the project, so the refresh of token and the request to ship token are all done using the interceptor of axios. There are three points to pay attention to:

Do not refresh token repeatedly, that is, one request has already refreshed token, at this time, the new token may not come back, and other requests should not be refreshed repeatedly. When the new token has not come back, other requests should be temporarily stored, and the request should be made again after the new token comes back. If the request is from the login page or the request itself is a request to refresh token, it does not need to be intercepted, otherwise it will fall into an infinite loop.

The first question can be locked with an Boolean field, and the second question can temporarily store the request initiated in the process of requesting a new token with Promise with pendding status and put it into an array. When the new token comes back, it can be resolve and Promise of every pendding in turn. Specific code details I directly paste the source code on the project:


import axios, * as AxiosInterface from 'axios';

// Token  Interface, access  token , refresh  token  And when it expires 
const instance = axios.create({
 // baseURL: ''
 timeout: 300000,
 headers: {
  'Content-Type': 'application/json',
  'X-Requested-With': 'XMLHttpRequest',
 },
});

async function refreshAccessToken(): Promise<AxiosInterface.AxiosResponse<AxiosData>> {
 return await instance.post('api/refreshtoken');
}

let isRefreshing = false;
let requests: Array<Function> = []; //  If in  token  Multiple requests coming in during the refresh process are stored in  requests  Medium 
// axios.defaults.baseURL = 'http://127.0.0.1:8888/api/private/v1/';
//  Set the request interceptor, if  token  Refresh if it expires  token
axios.interceptors.request.use(config => {
 const tokenObj = JSON.parse(window.localStorage.getItem('token') as string);
 if (config.url === 'api/login' || config.url === 'api/refreshtoken') return config;
 let accessToken = tokenObj.accessToken;
 let expireTime = tokenObj.expireTime;
 const refreshToken = tokenObj.refreshToken;

 config.headers.Authorization = accessToken;

 let time = Date.now();
 console.log(time, expireTime);
 if (time > expireTime) {
  if (!isRefreshing) {
   isRefreshing = true;
   refreshAccessToken()
    .then(res => {
     ({ accessToken, expireTime } = res.data.data);
     time = Date.now();
     const tokenStorage = {
      accessToken,
      refreshToken,
      expireTime: Number(time) + Number(expireTime),
     };
     window.localStorage.setItem('token', JSON.stringify(tokenStorage));
     isRefreshing = false;
     return accessToken;
    })
    .then((accessToken: string) => {
     requests.forEach(cb => {
      cb(accessToken);
     });
     requests = [];
    })
    .catch((err: string) => {
     throw new Error(`refresh token error: {err}`);
    });
  }

  //  If you are refreshing  token  The request made while the  requests  Array, you need to use the 1 A  pendding  Adj.  Promise  To ensure the success of the interception 
  const parallelRequest: Promise<AxiosInterface.AxiosRequestConfig> = new Promise(resolve => {
   requests.push((accessToken: string) => {
    config.headers.Authorization = accessToken;
    console.log(accessToken + Math.random() * 1000);
    resolve(config);
   });
  });

  return parallelRequest;
 }

 return config;
});

export default (vue: Function) => {
 vue.prototype.http = axios;
};

Summarize

The above is my implementation of refreshing token. If there are any mistakes, please correct me.

The above is the token mechanism and implementation of the details, more about the token mechanism and implementation of information please pay attention to other related articles on this site!


Related articles: