React应用中如何处理AWS Cognito Refresh Token?
React端AWS Cognito令牌管理与会话维持方案
一、令牌的安全存储
- 别用
localStorage存令牌,容易遭XSS攻击窃取,优先用sessionStorage——关闭浏览器标签就自动清除,安全性更高;如果后端支持,最好把令牌存在HttpOnly + Secure Cookie里,完全规避前端JS访问的风险。 - 登录成功后,把Cognito返回的
accessToken、idToken、refreshToken打包成JSON存在sessionStorage,同时存一下当前用户名(后续刷新会话要用)。
二、检查令牌有效性
1. 解析JWT手动判断过期
Cognito的accessToken和idToken都是JWT格式,直接解析payload里的exp字段(Unix时间戳)对比当前时间就行:
// 解析JWT的工具函数 const parseJwt = (token) => { try { return JSON.parse(atob(token.split('.')[1])); } catch (e) { return null; } }; // 检查令牌是否过期 const isTokenExpired = (token) => { if (!token) return true; const decoded = parseJwt(token); if (!decoded?.exp) return true; return decoded.exp * 1000 < Date.now(); };
- 每次发API请求前先跑这个检查,过期了就去刷新令牌。
2. 用Cognito SDK自带方法验证
如果用amazon-cognito-identity-js,可以直接用CognitoUserSession.isValid()方法验证整个会话的有效性:
import { CognitoUserSession } from 'amazon-cognito-identity-js'; // 从存储里取令牌构建会话对象 const storedSession = JSON.parse(sessionStorage.getItem('cognitoSession')); const session = new CognitoUserSession({ IdToken: storedSession.idToken, AccessToken: storedSession.accessToken, RefreshToken: storedSession.refreshToken, }); // 检查会话是否有效 const isSessionValid = session.isValid();
三、用RefreshToken刷新会话
当accessToken或idToken过期时,调用Cognito SDK的refreshSession方法获取新令牌:
import { CognitoUser } from 'amazon-cognito-identity-js'; import { userPool } from './your-cognito-config'; // 提前配置好的用户池实例 // 获取当前登录的Cognito用户对象 const getCurrentCognitoUser = () => { const username = sessionStorage.getItem('cognitoUsername'); return new CognitoUser({ Username: username, Pool: userPool }); }; // 刷新会话的异步方法 const refreshCognitoSession = async () => { const cognitoUser = getCurrentCognitoUser(); const storedRefreshToken = JSON.parse(sessionStorage.getItem('cognitoSession')).refreshToken; return new Promise((resolve, reject) => { cognitoUser.refreshSession(storedRefreshToken, (err, newSession) => { if (err) { // 刷新失败,比如refreshToken过期了,直接踢去登录页 reject(err); return; } // 更新存储里的令牌 const updatedTokens = { accessToken: newSession.getAccessToken().getJwtToken(), idToken: newSession.getIdToken().getJwtToken(), refreshToken: newSession.getRefreshToken().getToken(), }; sessionStorage.setItem('cognitoSession', JSON.stringify(updatedTokens)); resolve(newSession); }); }); };
四、会话维持的落地实践
- 统一请求拦截:在Axios或者Fetch的请求拦截器里处理令牌检查和刷新,避免每个请求都写一遍逻辑:
// Axios拦截器示例 import axios from 'axios'; let isRefreshing = false; // 防止多个请求同时触发刷新 let refreshQueue = []; axios.interceptors.request.use(async (config) => { const storedSession = sessionStorage.getItem('cognitoSession'); if (!storedSession) { window.location.href = '/login'; return Promise.reject(new Error('未检测到会话')); } const session = JSON.parse(storedSession); if (isTokenExpired(session.accessToken)) { if (isRefreshing) { // 等待当前刷新完成 return new Promise((resolve) => { refreshQueue.push(() => resolve(config)); }); } isRefreshing = true; try { await refreshCognitoSession(); const updatedSession = JSON.parse(sessionStorage.getItem('cognitoSession')); config.headers.Authorization = `Bearer ${updatedSession.accessToken}`; // 处理队列里的请求 refreshQueue.forEach(callback => callback(config)); refreshQueue = []; } catch (err) { sessionStorage.removeItem('cognitoSession'); sessionStorage.removeItem('cognitoUsername'); window.location.href = '/login'; return Promise.reject(err); } finally { isRefreshing = false; } } else { config.headers.Authorization = `Bearer ${session.accessToken}`; } return config; });
- 路由守卫:用React Router的导航守卫(比如在路由组件里加
useEffect,或者用Navigate组件),每次跳转前检查会话有效性,过期就跳登录页。 - RefreshToken有效期:Cognito默认给refreshToken设30天有效期,你可以在用户池的“应用客户端设置”里调整,一旦refreshToken过期,必须让用户重新登录(包括MFA验证)。
五、关键注意事项
- 绝对不要把
refreshToken存在localStorage里,XSS攻击很容易窃取,导致账号被盗。 - 前端的令牌检查只是为了提升用户体验,后端API必须自己验证
accessToken的有效性(比如通过Cognito的JWKS端点校验签名和过期时间),不能完全信任前端的判断。 - MFA登录后的会话处理和普通登录完全一致,用refreshToken刷新时不需要重新输入MFA验证码,除非你的用户池配置了强制MFA刷新。
内容的提问来源于stack exchange,提问作者Vyacheslav Fedorin
相关产品推荐
相关产品推荐

