基于AWS Cognito的自定义Node.js API授权方案问询
嘿,我来帮你理清楚怎么用Cognito给你的Node.js API做权限管控——结合你提到的API Gateway和Lambda的话,能实现更灵活、安全的权限体系,一步步来:
既然你已经能拿到Cognito的idToken/accessToken,第一步就是让你的API能验证这些token的合法性,再根据用户的身份信息做权限判断。
第一步:验证Token有效性
别自己手写签名验证,用成熟的库来做更靠谱。可以用jsonwebtoken结合jwks-rsa来拉取Cognito的公钥,验证token的签名、过期时间、发行方等信息。写个Express中间件就能复用:const jwt = require('jsonwebtoken'); const jwksClient = require('jwks-rsa'); // 替换成你的用户池信息 const client = jwksClient({ jwksUri: 'https://cognito-idp.<你的区域>.amazonaws.com/<你的用户池ID>/.well-known/jwks.json' }); function getSigningKey(header, callback) { client.getSigningKey(header.kid, (err, key) => { const signingKey = key.getPublicKey(); callback(null, signingKey); }); } // 验证token的中间件 const authenticateToken = (req, res, next) => { const authHeader = req.headers['authorization']; const token = authHeader?.split(' ')[1]; // 提取Bearer后面的token if (!token) return res.sendStatus(401); // 没有token直接返回未授权 jwt.verify(token, getSigningKey, { audience: '<你的应用客户端ID>', issuer: 'https://cognito-idp.<你的区域>.amazonaws.com/<你的用户池ID>' }, (err, decodedUser) => { if (err) return res.sendStatus(403); // token无效或过期 req.user = decodedUser; // 把解析后的用户信息挂载到req上 next(); }); };把这个中间件挂在需要授权的路由前面,比如
app.get('/api/protected', authenticateToken, (req, res) => { ... })。第二步:基于用户组/自定义属性做权限判断
Cognito的用户组(User Groups)是最省心的权限划分方式——你可以在Cognito控制台创建admin、editor、viewer这类组,把用户分配进去。解析后的token里会有cognito:groups字段,直接用这个来做权限校验:// 验证是否为admin的中间件 const requireAdmin = (req, res, next) => { if (!req.user['cognito:groups']?.includes('admin')) { return res.sendStatus(403); // 没有权限返回禁止访问 } next(); }; // 给admin专属路由用 app.get('/api/admin/dashboard', authenticateToken, requireAdmin, (req, res) => { res.send('欢迎访问admin控制面板'); });如果需要更灵活的标记,比如给用户加
custom:role这类自定义属性,原理一样,从req.user['custom:role']里取判断就行。
如果你的API可以迁移到Lambda,或者用API Gateway做代理,那完全可以把token验证和权限管控交给AWS服务来做,不用自己写代码维护:
步骤1:把Node.js API部署成Lambda(或用HTTP代理)
要么把你的Node.js代码打包成Lambda函数,要么用API Gateway的HTTP代理集成,直接指向你现有的API地址。步骤2:配置Cognito授权器
在API Gateway控制台里,给你的API添加一个Cognito用户池授权器,关联你的Cognito用户池和应用客户端。这样API Gateway会自动拦截请求,验证Authorization头里的Bearer token,无效请求直接返回401,不用你在代码里处理。步骤3:用IAM策略细化API权限
给Cognito用户组关联不同的IAM角色,每个角色对应不同的API访问权限。比如给admin组的角色配一个允许访问所有API资源的策略,给viewer组配只允许访问GET接口的策略:{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": "execute-api:Invoke", "Resource": "arn:aws:execute-api:<区域>:<账号ID>:<API ID>/<阶段>/GET/public/*" }, { "Effect": "Allow", "Action": "execute-api:Invoke", "Resource": "arn:aws:execute-api:<区域>:<账号ID>:<API ID>/<阶段>/*/admin/*" } ] }这样API Gateway会自动根据用户所属组的角色,判断是否允许访问当前请求的资源。
步骤4:在Lambda里获取用户信息
经过授权的请求,Lambda可以通过event.requestContext.authorizer.claims拿到用户的所有Cognito信息(比如组、自定义属性),然后在代码里做更细粒度的判断,比如用户只能访问自己创建的数据。
如果需要更复杂的权限逻辑(比如基于用户拥有的资源ID、动态权限),可以用API Gateway的自定义Lambda授权器:
- 写一个Lambda函数,接收API Gateway传来的token,验证后返回自定义的IAM策略,决定用户是否能访问当前请求的资源。
- 比如用户要访问
/api/articles/123,Lambda可以解析token里的用户ID,查询数据库确认用户是否拥有这篇文章的权限,再返回允许或拒绝的策略。
accessToken过期时间短(一般1小时),所以要在前端或API拦截器里用refreshToken去Cognito获取新的accessToken和idToken,避免用户频繁登录。用amazon-cognito-identity-js的示例代码:
const { CognitoUserPool } = require('amazon-cognito-identity-js'); const userPool = new CognitoUserPool({ UserPoolId: '<你的用户池ID>', ClientId: '<你的应用客户端ID>' }); const currentUser = userPool.getCurrentUser(); if (currentUser) { currentUser.getSession((err, session) => { if (err) { console.error('获取会话失败:', err); return; } if (!session.isValid()) { // 用refreshToken刷新会话 currentUser.refreshSession(session.getRefreshToken(), (err, newSession) => { if (err) { // 刷新失败,需要引导用户重新登录 console.error('刷新会话失败:', err); } else { const newIdToken = newSession.getIdToken().getJwtToken(); const newAccessToken = newSession.getAccessToken().getJwtToken(); // 保存新的token到本地存储或请求头 } }); } }); }
内容的提问来源于stack exchange,提问作者Vipul

