搭配Cognito用户池认证的React集成AWS API Gateway及token异常处理流程是怎样的
处理AWS Cognito Token过期与Axios请求流程方案
问题根因说明
AWS API Gateway集成Cognito授权器后,Token过期、无效时默认返回401 Unauthorized状态码,此类仅返回网络错误无明确提示的问题,通常是两个原因导致:
- API Gateway的CORS配置未放开401状态码与错误头的跨域暴露,前端无法识别正常的401响应,将其归类为跨域网络错误
- Axios自定义配置将401状态码判定为网络异常,未进入响应错误拦截逻辑
前置配置修正(先解决过期提示无法识别问题)
- 登录AWS API Gateway控制台,进入对应API的Cognito授权器配置,自定义未授权响应模板,返回体包含
"message": "Token is Expired"明确提示 - 调整API的CORS规则,在
Access-Control-Expose-Headers中添加WWW-Authenticate、x-amzn-ErrorType字段,允许401状态码的跨域返回 - 保留Axios默认的
validateStatus配置,仅将2xx状态码判定为成功响应,确保401可以正常进入响应错误拦截器
Axios全流程封装方案
以下方案基于AWS Amplify封装Cognito操作(也可自行替换为原生Cognito接口调用逻辑)
1. 初始化自定义Axios实例
import axios from 'axios'; import { Auth } from 'aws-amplify'; // Amplify会自动管理Cognito会话、Token存储 const apiClient = axios.create({ baseURL: '你的AWS API Gateway服务地址', timeout: 10000, validateStatus: (status) => status >= 200 && status < 300, // 仅2xx判定为成功响应 });
2. 请求拦截器统一注入Token
apiClient.interceptors.request.use( async (config) => { try { // Amplify的currentSession会自动校验Token有效性,过期时优先用Refresh Token刷新 const session = await Auth.currentSession(); const idToken = session.getIdToken().getJwtToken(); // 注入授权头,名称需和API Gateway Cognito授权器配置的头名称一致,默认为Authorization config.headers.Authorization = idToken; return config; } catch (err) { // 无法获取有效会话,直接跳转登录页 window.location.href = '/login'; return Promise.reject(err); } }, (error) => Promise.reject(error) );
3. 响应拦截器处理Token过期逻辑
// 标记是否正在刷新Token,避免重复调用刷新接口 let isRefreshing = false; // 存储刷新Token期间排队的请求 let pendingRequests = []; apiClient.interceptors.response.use( (response) => response.data, // 正常响应直接返回数据 async (error) => { const originalRequest = error.config; // 判定为Token过期:返回401且当前请求未重试 if (error.response?.status === 401 && !originalRequest._retry) { if (isRefreshing) { // 正在刷新Token,将当前请求加入队列等待 return new Promise(resolve => { pendingRequests.push((newToken) => { originalRequest.headers.Authorization = newToken; resolve(apiClient(originalRequest)); }); }); } originalRequest._retry = true; isRefreshing = true; try { // 触发Token刷新,Amplify自动用Refresh Token换取新的ID Token const session = await Auth.currentSession(); const newIdToken = session.getIdToken().getJwtToken(); // 更新全局默认授权头 apiClient.defaults.headers.common.Authorization = newIdToken; // 执行所有排队的请求 pendingRequests.forEach(callback => callback(newIdToken)); pendingRequests = []; // 重试当前失败的请求 return apiClient(originalRequest); } catch (refreshErr) { // 刷新失败说明Refresh Token也已过期,清空会话跳转登录 await Auth.signOut(); window.location.href = '/login'; return Promise.reject(refreshErr); } finally { isRefreshing = false; } } // 其他错误直接抛出给业务层处理 return Promise.reject(error); } );
4. 封装通用请求方法适配各类请求
// GET请求 export const get = (url, params = {}, config = {}) => { return apiClient.get(url, { params, ...config }); }; // POST请求 export const post = (url, data = {}, config = {}) => { return apiClient.post(url, data, config); }; // PUT请求 export const put = (url, data = {}, config = {}) => { return apiClient.put(url, data, config); }; // DELETE请求 export const del = (url, params = {}, config = {}) => { return apiClient.delete(url, { params, ...config }); };
5. 业务层使用示例
import { get, post } from './封装的axios文件路径'; // 调用GET接口 const fetchUserInfo = async () => { try { const userInfo = await get('/user/detail', { userId: 1001 }); console.log('用户信息', userInfo); } catch (err) { console.error('请求失败', err); } }; // 调用POST接口 const createOrder = async (orderData) => { try { const result = await post('/order/create', orderData); console.log('创建成功', result); } catch (err) { console.error('创建失败', err); } };
额外优化点
- 可在请求拦截器注入Token前,本地校验Token的
exp时间戳,若已过期直接触发刷新逻辑,减少一次无效请求 - 若不使用AWS Amplify,可自行调用Cognito的
InitiateAuth接口传入Refresh Token换取新的ID/Access Token - 敏感场景下建议Token存储在
sessionStorage而非localStorage,降低XSS攻击泄露风险
内容的提问来源于stack exchange,提问作者Yash Patel
相关产品推荐
相关产品推荐

