“This article has participated in the good article call order activity, click to see: back end, big front end double track submission, 20,000 yuan prize pool for you to challenge!

preface

Recently, when I was doing the requirements, the product asked a question about the login token: can the token expire for a longer time? I need to log in frequently.

Front end: back end, can you set the token expiration time a little longer.

Back end: Yes, but that’s not safe. You can do better.

Front end: What method?

Back end: the interface that gives you a token refresh to refresh the token periodically

Front end: Ok, let me think about it

demand

When the token expires, refresh the token. The front-end needs to refresh the token insensitively, that is, the user should not be aware of the token brushing to avoid frequent login. Implementation approach

  • Methods a

The back end returns the expiration time, and the front end determines the token expiration time and calls the refresh token interface

Disadvantages: Requires the backend to provide an additional token expiration time field; The interception will fail if the local time is tampered with, especially if the local time is slower than the server time.

  • Method 2

Write a timer to refresh the token interface periodically

Disadvantages: It is not recommended because it wastes resources and consumes performance.

  • Methods three

After intercepting in the response interceptor, the token returns expire and invokes the refresh token interface

implementation

Axios basic skeleton, the use of the service. The interceptors. The response to intercept

import axios from 'axios'

service.interceptors.response.use(
  response => {
    if (response.data.code === 409) {
        return refreshToken({ refreshToken: localStorage.getItem('refreshToken'), token: getToken() }).then(res => {
          const { token } = res.data
          setToken(token)
          response.headers.Authorization = `${token}`
        }).catch(err => {
          removeToken()
          router.push('/login')
          return Promise.reject(err)
        })
    }
    return response && response.data
  },
  (error) => {
    Message.error(error.response.data.msg)
    return Promise.reject(error)
  }
)
Copy the code

Problem solving

Question 1: How do I prevent multiple token refreshes

We control whether the status of the token isRefreshing by using a variable isRefreshing.

import axios from 'axios' service.interceptors.response.use( response => { if (response.data.code === 409) { if (! isRefreshing) { isRefreshing = true return refreshToken({ refreshToken: localStorage.getItem('refreshToken'), token: getToken() }).then(res => { const { token } = res.data setToken(token) response.headers.Authorization = `${token}` }).catch(err => { removeToken() router.push('/login') return Promise.reject(err) }).finally(() => { isRefreshing = false  }) } } return response && response.data }, (error) => { Message.error(error.response.data.msg) return Promise.reject(error) } )Copy the code

Question 2: When two or more requests are sent at the same time, how can other interfaces solve the problem

When the second expired request comes in and the token is being refreshed, we first store the request in an array queue and try to keep the request waiting until the token is refreshed and then try to clear the request queue one by one. So how do you keep the request waiting? To solve this problem, we had to turn to Promise. We queued the request and returned a Promise, leaving the Promise in the Pending state. The request will wait and wait as long as we do not call resolve. When the refreshed interface returns, we call resolve again and try again one by one. Final code:

Import axios from 'axios' // Let isRefreshing = false // Retry queue let requests = [] Service. The interceptors. Response. Use (the response code 409 token expired = > {/ / agreed the if (response. Data. Code = = = 409) {if (! IsRefreshing) {isRefreshing = true / / call the refresh token interface return refreshToken ({refreshToken: localStorage.getItem('refreshToken'), token: GetToken ()}). Then (res = > {const {token} = res. Data / / replace token setToken (token). The response headers. The Authorization = ForEach ((cb) => cb(token)) requests = [ Service (response.config)}).catch(err => {removeToken() router-push ('/login') return promise.reject (err) }). Finally (() => {isRefreshing = false})} else {// Return the Promise(resolve => {isRefreshing = false}) Resolve, Waiting to refresh and then perform requests. Push (token = > {response. Headers. Authorization = ${token} ` ` resolve (service (response. Config))})})} } return response && response.data }, (error) => { Message.error(error.response.data.msg) return Promise.reject(error) } )Copy the code

The last

The product needs are completed, I don’t know if there is a better solution, you can comment in the comments area, say what you do.

Popular articles on previous interview topics

  • HTML + CSS interview: the front end of the interview in June 2021 | HTML + CSS
  • Js interview questions: 30 JS interview questions to help me sprint “20K”, rustling
  • Vue Interview questions: Answer these VUE interview questions correctly, am I a qualified intermediate front-end development engineer?
  • Interviewer: Can you write some basic operations of the list by hand