AWS Gateway V2 WebSocket $connect路径返回要求及Lambda授权器格式问题
兄弟,我之前踩过一模一样的坑!AWS API Gateway V2的WebSocket授权器返回格式要求特别严格,还有$connect路由的规则也容易搞混,我给你梳理清楚:
一、REQUEST类型Lambda授权器的正确返回格式
你之前试的返回claims或者{"statusCode":200}都不符合要求——REQUEST类型的授权器必须返回包含principalId和policyDocument的JSON结构,这是Gateway用来判断是否允许连接的核心依据。
正确的返回示例(验证通过时):
{ "principalId": "your-cognito-user-sub", // 用Cognito令牌里的sub字段最合理 "policyDocument": { "Version": "2012-10-17", // 固定值,不能改 "Statement": [ { "Action": "execute-api:Invoke", // 固定值 "Effect": "Allow", // 允许连接用Allow,拒绝用Deny "Resource": "arn:aws:execute-api:us-east-1:1234567890:abc123/prod/$connect" // 必须是当前请求的$connect路径ARN } ] }, "context": { // 可选,用来传递自定义数据给后端集成 "username": "johndoe", "customAttr": "value" } }
验证失败时,只要把Effect改成Deny就行,Gateway会直接返回403给客户端。
为什么你的之前返回无效?
- 直接返回claims对象:Gateway完全不识别这个格式,会抛出500错误
- 返回
{"statusCode":200}:同样不符合授权器的规范,Gateway无法解析,导致500 - 出现403:要么是你返回了
Deny策略,要么是Resource的ARN写错了(必须精确匹配当前的API、阶段和$connect路径)
二、WebSocket $connect路径的返回要求
当授权器通过后,$connect路由的后端集成(比如你的Lambda)返回格式很宽松:
- 只要返回
{"statusCode":200}就行,Gateway会忽略返回内容的其他部分,直接建立WebSocket连接 - 如果授权器返回
Deny,Gateway会直接返回403,根本不会调用你的后端集成 - 注意:$connect路由必须配置授权器,否则默认是拒绝所有连接(除非你开了无授权访问)
三、给你的完整Lambda授权器示例(Node.js)
结合你用Cognito JWKS验证令牌的场景,给你写个可复用的示例:
const jwt = require('jsonwebtoken'); const jwksClient = require('jwks-rsa'); // 替换成你的Cognito用户池JWKS地址 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); }); } exports.handler = async (event) => { try { // 从headers提取Bearer令牌 const authHeader = event.headers.Auth; if (!authHeader || !authHeader.startsWith('Bearer ')) { return generateAuthResponse('unauthorized', 'Deny', event.routeArn); } const token = authHeader.split(' ')[1]; // 验证JWT签名和内容 const decoded = await new Promise((resolve, reject) => { jwt.verify(token, getSigningKey, { audience: '你的Cognito应用客户端ID', issuer: 'https://cognito-idp.你的区域.amazonaws.com/你的用户池ID' }, (err, decoded) => { if (err) reject(err); else resolve(decoded); }); }); // 验证通过,返回允许策略 return generateAuthResponse(decoded.sub, 'Allow', event.routeArn); } catch (err) { console.error('授权失败:', err); // 验证失败返回拒绝策略 return generateAuthResponse('unauthorized', 'Deny', event.routeArn); } }; // 生成标准授权响应的工具函数 function generateAuthResponse(principalId, effect, resource) { return { principalId, policyDocument: { Version: '2012-10-17', Statement: [{ Action: 'execute-api:Invoke', Effect: effect, Resource: resource }] } }; }
四、Terraform配置的关键检查点
确保你的aws_apigatewayv2_authorizer配置正确:
resource "aws_apigatewayv2_authorizer" "websocket_auth" { api_id = aws_apigatewayv2_api.your_websocket_api.id name = "websocket-request-authorizer" type = "REQUEST" authorizer_type = "REQUEST" authorizer_uri = aws_lambda_function.your_auth_lambda.invoke_arn identity_source = ["$request.header.Auth"] // 对应你取令牌的header字段 authorizer_payload_format_version = "1.0" // WebSocket授权器必须用1.0,2.0是给HTTP API的 } // 别忘了给Lambda加允许Gateway调用的权限 resource "aws_lambda_permission" "gateway_auth_invocation" { statement_id = "AllowAPIGatewayInvoke" action = "lambda:InvokeFunction" function_name = aws_lambda_function.your_auth_lambda.function_name principal = "apigateway.amazonaws.com" source_arn = "${aws_apigatewayv2_api.your_websocket_api.execution_arn}/*/$connect" }
按这个配置走,wscat的连接应该就能正常通过授权了!
内容的提问来源于stack exchange,提问作者warrens
相关产品推荐
相关产品推荐

