React.js中如何利用Cookie内的Refresh Token刷新Access Token?
Access/Refresh Token机制及前端刷新实现指南
一、Access Token与Refresh Token通用运行机制
核心是平衡安全性和用户体验,流程如下:
- 首次登录:用户提交账号密码到认证服务器,验证通过后生成两个Token:
- Access Token:短期有效(建议15-30分钟),用于调用业务接口,需放在请求头
Authorization: Bearer xxx中。 - Refresh Token:长期有效(建议7-30天),仅用于向认证服务器获取新的Access Token,通常存入HttpOnly、Secure的Cookie(防止前端JS读取,规避XSS风险)。
- Access Token:短期有效(建议15-30分钟),用于调用业务接口,需放在请求头
- 日常请求:前端调用业务接口时携带Access Token,业务服务器校验Token的签名、过期时间,有效则处理请求,无效返回401。
- 刷新流程:当Access Token过期(接口返回401),前端触发刷新请求,浏览器自动携带Cookie中的Refresh Token到认证服务器;服务器校验通过后返回新的Access Token(可选同步更新Refresh Token);若Refresh Token也过期,直接引导用户重新登录。
二、利用Cookie中的Refresh Token刷新Access Token
后端配置(Node.js)
将Refresh Token设置为HttpOnly Cookie,确保前端无法通过JS读取,仅由浏览器自动携带:
// 登录成功后设置Refresh Token Cookie res.cookie('refreshToken', generatedRefreshToken, { httpOnly: true, // 关键:禁止前端JS读取 secure: process.env.NODE_ENV === 'production', // 生产环境仅HTTPS传输 sameSite: 'strict', // 防范CSRF攻击 maxAge: 30 * 24 * 60 * 60 * 1000 // 30天有效期 });
前端实现(React.js)
无需手动读取Cookie,直接调用刷新接口即可,浏览器会自动携带Refresh Token:
// 示例:Axios请求刷新Token async function refreshAccessToken() { try { const response = await axios.post('/api/auth/refresh'); return response.data.accessToken; // 获取新的Access Token } catch (error) { // 刷新失败,跳转登录页 window.location.href = '/login'; throw error; } }
三、前端刷新请求的频率与性能优化(避免内存泄漏)
为什么不推荐固定setInterval
固定时间间隔的setInterval会引发一系列问题:
- 不必要的请求:用户退出或Token已刷新后,定时器仍在运行。
- 内存泄漏:组件卸载时未清除定时器,导致定时器持续占用内存。
- 性能下降:频繁触发定时器+状态更新,引发不必要的组件重渲染。
推荐两种优化方案
1. 接口拦截器按需刷新(最优)
在Axios/Fetch的响应拦截器中统一处理401,仅当接口返回Token过期时触发刷新,完全按需:
// Axios响应拦截器示例 axios.interceptors.response.use( (response) => response, async (error) => { const originalRequest = error.config; // 避免刷新接口本身的401导致死循环 if (error.response.status === 401 && !originalRequest._retry) { originalRequest._retry = true; try { const newAccessToken = await refreshAccessToken(); // 更新全局请求头的Token axios.defaults.headers.common['Authorization'] = `Bearer ${newAccessToken}`; // 重新发起原请求 return axios(originalRequest); } catch (refreshError) { return Promise.reject(refreshError); } } return Promise.reject(error); } );
2. 基于Token过期时间的延迟刷新(提前刷新场景)
若需提前刷新(比如提前5分钟),用setTimeout替代setInterval,且在组件卸载时清除定时器:
import { useEffect, useState } from 'react'; import axios from 'axios'; function AuthComponent() { const [accessToken, setAccessToken] = useState(localStorage.getItem('accessToken')); useEffect(() => { let timeoutId; if (accessToken) { // 解析JWT的过期时间(JWT payload为base64编码) const payload = JSON.parse(atob(accessToken.split('.')[1])); const expiresTimestamp = payload.exp * 1000; // exp是秒级时间戳,转毫秒 const refreshTime = expiresTimestamp - Date.now() - 5 * 60 * 1000; // 提前5分钟刷新 if (refreshTime > 0) { timeoutId = setTimeout(async () => { const newToken = await refreshAccessToken(); setAccessToken(newToken); localStorage.setItem('accessToken', newToken); }, refreshTime); } } // 组件卸载时清除定时器,避免内存泄漏 return () => { if (timeoutId) clearTimeout(timeoutId); }; }, [accessToken]); // ...其他组件逻辑 }
这种方式每次Access Token更新后会重新计算超时时间,仅维持一个定时器运行,不会出现叠加问题,也能规避内存泄漏。
内容的提问来源于stack exchange,提问作者ILOVEJSON
相关产品推荐
相关产品推荐

