Angular中JWT刷新令牌的正确实现方式及问题咨询
Hey there! Let's work through your JWT refresh token implementation issues. Based on your requirements and common pitfalls folks run into with this flow, here are solutions to the most likely problems you're facing:
1. Concurrent Requests Triggering Multiple Refresh Calls
This is the most frequent pain point: when multiple requests hit a 401 token expired error at the same time, your code might fire multiple refresh token requests, leading to redundant API calls or token conflicts.
Fix: Use a Promise-based lock to ensure only one refresh operation runs at a time, and queue other failed requests until the refresh completes.
Here's a practical Axios interceptor example (works for most frontend setups):
let isRefreshing = false; let failedRequestQueue = []; // Helper to process queued requests once refresh succeeds/fails const processQueuedRequests = (error, newToken = null) => { failedRequestQueue.forEach(promise => { if (error) { promise.reject(error); } else { promise.resolve(newToken); } }); failedRequestQueue = []; }; // Attach response interceptor axios.interceptors.response.use( response => response, async error => { const originalRequest = error.config; // Only trigger refresh if it's a token-expired 401, and we haven't retried yet if ( error.response?.status === 401 && error.response.data.message === "JWT Token Expired" && !originalRequest._retry ) { if (isRefreshing) { // Wait for ongoing refresh to finish, then retry the request return new Promise((resolve, reject) => { failedRequestQueue.push({ resolve, reject }); }) .then(token => { originalRequest.headers['Authorization'] = `Bearer ${token}`; return axios(originalRequest); }) .catch(err => Promise.reject(err)); } // Mark request as retried to avoid loops originalRequest._retry = true; isRefreshing = true; try { // Call your refresh token endpoint const refreshResponse = await axios.post('/api/auth/refresh', { refreshToken: localStorage.getItem('refreshToken') }); const newAccessToken = refreshResponse.data.accessToken; // Update stored tokens and default headers localStorage.setItem('accessToken', newAccessToken); axios.defaults.headers.common['Authorization'] = `Bearer ${newAccessToken}`; // Retry all queued requests with the new token processQueuedRequests(null, newAccessToken); // Retry the original failed request return axios(originalRequest); } catch (refreshError) { // Refresh failed: clear tokens and redirect to login localStorage.removeItem('accessToken'); localStorage.removeItem('refreshToken'); processQueuedRequests(refreshError, null); window.location.href = '/login'; return Promise.reject(refreshError); } finally { isRefreshing = false; } } // For non-token-expiry 401s or other errors, reject normally return Promise.reject(error); } );
2. Catching Non-Expiry 401s by Mistake
Not all 401s mean the token is expired—your backend might return 401 for invalid tokens, missing permissions, or other reasons. If you trigger refresh for every 401, you'll waste API calls and confuse users.
Fix: Strictly validate the error reason. Check for a specific message (like "JWT Token Expired") or a custom error code (e.g., error.response.data.errorCode === "TOKEN_EXPIRED") instead of just relying on the 401 status.
3. Losing Original Request Context
When retrying the failed request, you might accidentally drop custom headers, request body data, or HTTP method details, leading to invalid retries.
Fix: Preserve the full originalRequest object. In the code above, we pass originalRequest directly to axios()—this retains all the original configuration (headers, body, method, etc.).
4. Not Handling Refresh Token Expiry
If the refresh token itself is expired, calling the refresh endpoint will fail. You need to handle this case explicitly instead of looping indefinitely.
Fix: In the catch block of the refresh call, clear all stored tokens and redirect immediately to the login page, as shown in the example.
5. Missing Retry Flag to Prevent Loops
Without marking the original request as retried, if the new access token somehow expires immediately (edge case), your code could get stuck in an infinite refresh loop.
Fix: Add a _retry flag to the original request config, so we only attempt to refresh and retry once per request.
Quick Best Practices
- Store refresh tokens securely: Avoid
localStorageif possible—use HttpOnly, secure cookies to mitigate XSS risks. - Validate the new access token before using it (check its expiry date or signature if needed).
- Add loading states in your UI to prevent users from triggering duplicate requests while a refresh is in progress.
内容的提问来源于stack exchange,提问作者Sergej

