Vue SPA中Axios拦截器Token更新后请求复用旧Token问题排查
Hey, let's figure out why your updated token isn't being picked up by subsequent requests and fix it step by step.
First, the core issue here is: your request interceptor reads the token from localStorage at the moment the request is initiated. When you update localStorage in the response interceptor, any pending requests have already passed the request interception stage and are still carrying the old token. Plus, your 401 retry logic has potential race conditions that prevent it from grabbing the latest token reliably.
We can fix this by maintaining a global token variable, which ensures all requests get the most up-to-date token in real time instead of relying on the timing of localStorage reads.
Here's the adjusted complete code:
// Initialize global token variable with the value from localStorage let currentToken = window.localStorage.getItem('authToken'); // Request interceptor: Use the global token to set the request header axios.interceptors.request.use(config => { if (currentToken) { config.headers.Authorization = `Bearer ${currentToken}`; } return Promise.resolve(config); }); // Response interceptor: Handle new token returns and 401 retries axios.interceptors.response.use( response => { // Check if the response carries a new token if (response.data.meta?.tokens?.Bearer) { const newToken = response.data.meta.tokens.Bearer; console.log('Setting new token:', newToken); // Update both the global variable and localStorage to keep them in sync currentToken = newToken; window.localStorage.setItem('authToken', newToken); } return response; }, error => { const originalRequest = error.config; // Only handle 401 errors that haven't been retried yet to avoid infinite loops if (error.response?.status === 401 && !originalRequest._retry) { originalRequest._retry = true; // Sync the global token with localStorage as a safeguard against inconsistencies currentToken = window.localStorage.getItem('authToken'); if (currentToken) { // Update the original request's Authorization header with the latest token originalRequest.headers.Authorization = `Bearer ${currentToken}`; // Resend the request return axios(originalRequest); } else { // If no valid token exists, redirect to login or handle authentication logic here // Example: router.push('/login'); return Promise.reject(error); } } return Promise.reject(error); } );
Why this works:
- Global token variable: The request interceptor uses this variable directly instead of reading from
localStorageevery time. When the response interceptor updates this variable, all subsequent requests (including retries) immediately use the latest token, eliminating lag. - Synchronized updates: When a new token is received, we update both the global variable and
localStorageto ensure data consistency—this keeps the token persisted while maintaining real-time availability. - Optimized retry logic: Before retrying, we sync the global token with
localStorageto guarantee we're using the latest value, then update the original request's header to avoid failing again with the old token.
If you ever run into scenarios where multiple requests trigger 401 at the same time (like when a token expires mid-concurrent requests), you could add a lock mechanism to avoid redundant retries. But based on your description of the server returning tokens in responses, this might not be necessary right now.
内容的提问来源于stack exchange,提问作者howlCode

