preface
Recently, when making requirements, regarding login token, the product raised a question: 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 to be longer?
Back end: Yes, but it’s not safe to do that. You can do better.
Front end: What method?
Back end: gives you the interface to refresh the token, periodically refresh the token
Front end: Ok, let me think about this
demand
When the token expires, the front-end must refresh the token senlessly. That is, users must not feel the token when brushing the token to avoid frequent login. Implementation approach
Methods a
The backend returns the expiration time, and the front-end determines the token expiration time and invokes the token refresh interface
Disadvantages: The backend needs to provide an additional token expiration time field; If the local time is tampered, especially if the local time is slower than the server time, interception will fail.
Method 2
Write a timer to refresh the token interface periodically
Disadvantages: Waste resources and consume performance, not recommended.
Methods three
After intercepting in the response interceptor and determining that the token return is expired, the refresh token interface is called
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 the token from being refreshed multiple times
We control whether the token status is being refreshed 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 do other interfaces solve the problem
When the second expired request comes in and the token is being refreshed, we store the request in an array queue, try to keep the request on hold, wait until the token is refreshed, and then try to clear the queue one by one. So how do you keep the request on hold? To solve this problem, we need to use Promise. After we queue the request, we return a Promise that will remain Pending (resolve will not be called), and the request will wait and wait as long as resolve is not executed. When the refresh request interface returns, we call resolve and retry each one. Final code:
import axios from 'axios'
// The flag that is being refreshed
let isRefreshing = false
// Retry queue
let requests = []
service.interceptors.response.use(
response= > {
// Code 409 token expires
if (response.data.code === 409) {
if(! isRefreshing) { isRefreshing =true
// Invoke the refresh token interface
return refreshToken({ refreshToken: localStorage.getItem('refreshToken'), token: getToken() }).then(res= > {
const { token } = res.data
/ / replace token
setToken(token)
response.headers.Authorization = `${token}`
}).catch(err= > {
// Jump to the login page
removeToken()
router.push('/login')
return Promise.reject(err)
}).finally(() = > {
isRefreshing = false})}else {
// Return the Promise that resolve is not executed
return new Promise(resolve= > {
// Store resolve as a function and wait to refresh
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