React Native应用:后端存储AWS Cognito用户令牌与信息的实现问询
我来帮你拆解这个方案的具体实现步骤,分前端(React Native)和后端两部分来说,这样你能一步步落地:
整体流程对齐
先再明确下你要的代理模式:
- 请求链路:React Native 前端 → 你的后端API → AWS Cognito
- 响应链路:AWS Cognito → 你的后端API → React Native 前端
核心是让后端作为中间层,全权代理Cognito的认证交互,同时把令牌和用户信息落地到自己的数据库里,方便后续业务调用。
后端实现步骤(以Node.js/Express为例,可适配其他后端栈)
1. 配置AWS SDK连接Cognito
首先安装AWS SDK,然后初始化Cognito服务客户端:
const AWS = require('aws-sdk'); // 生产环境建议用IAM角色(比如ECS/EKS执行角色、Lambda角色),不要硬编码密钥 AWS.config.update({ region: '你的Cognito区域(如us-east-1)', accessKeyId: process.env.AWS_ACCESS_KEY_ID, secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY }); const cognitoISP = new AWS.CognitoIdentityServiceProvider();
2. 编写代理Cognito认证的API端点
以登录接口为例,前端把用户名密码传给后端,后端调用Cognito接口完成认证,拿到令牌和用户信息后存入自有的数据库:
app.post('/api/auth/login', async (req, res) => { const { username, password } = req.body; const cognitoParams = { AuthFlow: 'USER_PASSWORD_AUTH', ClientId: '你的Cognito App Client ID', AuthParameters: { USERNAME: username, PASSWORD: password } }; try { // 调用Cognito完成登录 const authResult = await cognitoISP.initiateAuth(cognitoParams).promise(); const { IdToken, AccessToken, RefreshToken, ExpiresIn } = authResult.AuthenticationResult; // 获取用户详细属性 const userDetails = await cognitoISP.getUser({ AccessToken }).promise(); // 把令牌和用户信息存入你的数据库(示例用Sequelize ORM,可换成你用的数据库工具) await UserModel.upsert({ username: userDetails.Username, id_token: IdToken, // 建议加密存储 access_token: AccessToken, // 建议加密存储 refresh_token: RefreshToken, // 建议加密存储 user_attributes: userDetails.UserAttributes, token_expires_at: new Date(Date.now() + ExpiresIn * 1000) }); // 返回给前端必要信息(比如只返回IdToken和用户属性,避免暴露过多敏感内容) res.json({ success: true, idToken: IdToken, user: userDetails.UserAttributes.reduce((acc, attr) => { acc[attr.Name] = attr.Value; return acc; }, {}) }); } catch (error) { // 把Cognito的错误转化为友好提示返回给前端 res.status(401).json({ success: false, message: error.message.includes('UserNotFoundException') ? '用户不存在' : '用户名或密码错误' }); } });
3. 实现令牌刷新逻辑
当前端的Access Token过期时,调用后端的刷新接口,后端用Refresh Token向Cognito换取新令牌,更新数据库后返回给前端:
app.post('/api/auth/refresh', async (req, res) => { const { refreshToken } = req.body; const cognitoParams = { AuthFlow: 'REFRESH_TOKEN_AUTH', ClientId: '你的Cognito App Client ID', AuthParameters: { REFRESH_TOKEN: refreshToken } }; try { const authResult = await cognitoISP.initiateAuth(cognitoParams).promise(); const newIdToken = authResult.AuthenticationResult.IdToken; const newAccessToken = authResult.AuthenticationResult.AccessToken; // 更新数据库中的令牌信息 await UserModel.update( { id_token: newIdToken, access_token: newAccessToken, token_expires_at: new Date(Date.now() + authResult.AuthenticationResult.ExpiresIn * 1000) }, { where: { refresh_token: refreshToken } } ); res.json({ success: true, idToken: newIdToken, accessToken: newAccessToken }); } catch (error) { res.status(401).json({ success: false, message: '刷新令牌无效,请重新登录' }); } });
React Native前端实现步骤
1. 调用后端认证接口,存储令牌
不要直接调用Cognito SDK,所有认证请求都走自己的后端:
import axios from 'axios'; import AsyncStorage from '@react-native-async-storage/async-storage'; // 登录函数 export const login = async (username, password) => { try { const response = await axios.post('/api/auth/login', { username, password }); // 把令牌和用户信息存在本地(建议用MMKV替代AsyncStorage,性能更好) await AsyncStorage.setItem('idToken', response.data.idToken); await AsyncStorage.setItem('userInfo', JSON.stringify(response.data.user)); return response.data; } catch (error) { throw new Error(error.response.data.message); } };
2. 用令牌请求后端业务接口
后续请求用户相关数据时,带上Id Token,后端会验证令牌有效性:
// 获取用户个人信息 export const getUserProfile = async () => { const idToken = await AsyncStorage.getItem('idToken'); try { const response = await axios.get('/api/user/profile', { headers: { Authorization: `Bearer ${idToken}` } }); return response.data; } catch (error) { // 处理令牌过期,自动刷新后重试 if (error.response.status === 401) { const refreshToken = await AsyncStorage.getItem('refreshToken'); const refreshRes = await axios.post('/api/auth/refresh', { refreshToken }); await AsyncStorage.setItem('idToken', refreshRes.data.idToken); // 重新请求接口 return getUserProfile(); } throw error; } };
3. 注销逻辑
前端调用后端注销接口,后端通知Cognito登出并清理数据库中的令牌:
// 后端注销接口 app.post('/api/auth/logout', async (req, res) => { const { accessToken } = req.body; try { // 通知Cognito全局登出 await cognitoISP.globalSignOut({ AccessToken: accessToken }).promise(); // 清理数据库中的令牌 await UserModel.update( { id_token: null, access_token: null, refresh_token: null }, { where: { access_token: accessToken } } ); res.json({ success: true }); } catch (error) { res.status(500).json({ success: false, message: '注销失败' }); } }); // 前端注销函数 export const logout = async () => { const accessToken = await AsyncStorage.getItem('accessToken'); await axios.post('/api/auth/logout', { accessToken }); // 清理本地存储 await AsyncStorage.multiRemove(['idToken', 'accessToken', 'refreshToken', 'userInfo']); };
关键注意事项
- 安全优先:
- 后端与Cognito、前端与后端的通信必须用HTTPS;
- 数据库中存储的令牌要加密,不要明文存储;
- 后端要验证令牌的有效性(可以调用Cognito的
getUser接口,或者直接验证JWT签名);
- 性能优化:
- 可以缓存Cognito返回的用户信息,减少重复调用Cognito API的次数;
- 合理设置令牌过期时间(Cognito默认Access Token有效期1小时,Refresh Token30天);
- 错误处理:
- 针对Cognito的不同错误码(如
UserNotFoundException、NotAuthorizedException)返回友好提示; - 前端要处理令牌过期、网络异常等场景,给用户清晰的操作指引。
- 针对Cognito的不同错误码(如
内容的提问来源于stack exchange,提问作者sejn
相关产品推荐
相关产品推荐

