为什么我的 API Gateway 代理资源使用已激活缓存的 Lambda 授权方返回 HTTP 403“User is not authorized to access this resource(用户无权访问此资源)”错误?
我的 Amazon API Gateway 代理资源使用已激活缓存的 AWS Lambda 授权方返回以下 HTTP 403 错误消息:“User is not authorized to access this resource(用户无权访问此资源)”。为什么会出现这种情况,我该怎样解决这个错误呢?
简短描述
**注意:**API Gateway 可以因各种原因返回 403 User is not authorized to access this resource(用户无权访问此资源)错误。本文解决了与使用仅激活缓存的 Lambda 授权方的 API Gateway 代理资源相关的 403 错误。有关排查其他类型 403 错误的信息,请参阅如何排查来自 API Gateway 的 HTTP 403 错误?
Lambda 授权方的输出会向 API Gateway 返回 AWS Identity and Access Management(IAM)策略。IAM policy 包含一个显式 API Gateway API“Resource”(资源)元素,其格式如下:
"arn:aws:execute-api:<region>:<account>:<API_id>/<stage>/<http-method>/[<resource-path-name>/[<child-resources-path>]"
在 Lambda 授权方激活 Authorization Caching(授权缓存)时,返回的 IAM policy 将被缓存。然后,缓存的 IAM policy 将应用于在缓存的指定存活时间(TTL)期限内发出的任何其他 API 请求。
如果 API 的代理资源的贪婪路径变量为 {proxy+},则第一次授权成功。在缓存的 TTL 周期内向其他路径发出的任何其他 API 请求都会失败,并返回以下错误:
"message": "User is not authorized to access this resource"(“消息”:“用户无权访问此资源”)
额外的请求会失败,因为路径与缓存的 IAM policy 中定义的显式 API Gateway API**“Resource”**(资源)元素不匹配。
要解决此问题,您可以修改 Lambda 授权方函数的代码,改为在输出中返回通配符(*/*)资源。有关更多信息,请参阅 Lambda 操作的资源和条件。
注意:要激活授权方缓存,您的授权方必须返回一个适用于 API Gateway 中所有方法的策略。Lambda 授权方函数的代码必须在输出中返回通配符(*/*)资源才能允许所有资源。缓存策略需要缓存相同的资源路径,除非您在同一资源路径上两次发出相同的请求。
解决方法
**注意:**修改本文中的示例 Lambda 授权方函数代码段以适配您的使用案例。
在以下示例设置中,Lambda 函数从方法的 Amazon 资源名称(ARN)(“event.methodArn”)中提取 API Gateway 的 id 值。然后,函数通过将方法 ARN 的路径与 API 的 id 值和通配符(*/*)组合来定义通配符**“Resource”**(资源)变量。
返回通配符“Resource”(资源)变量且基于令牌的 Lambda 授权方函数代码示例
exports.handler = function(event, context, callback) { var token = event.authorizationToken; var tmp = event.methodArn.split(':'); var apiGatewayArnTmp = tmp[5].split('/'); // Create wildcard resource var resource = tmp[0] + ":" + tmp[1] + ":" + tmp[2] + ":" + tmp[3] + ":" + tmp[4] + ":" + apiGatewayArnTmp[0] + '/*/*'; switch (token) { case 'allow': callback(null, generatePolicy('user', 'Allow', resource)); break; case 'deny': callback(null, generatePolicy('user', 'Deny', resource)); break; case 'unauthorized': callback("Unauthorized"); // Return a 401 Unauthorized response break; default: callback("Error: Invalid token"); // Return a 500 Invalid token response } }; // Help function to generate an IAM policy var generatePolicy = function(principalId, effect, resource) { var authResponse = {}; authResponse.principalId = principalId; if (effect && resource) { var policyDocument = {}; policyDocument.Version = '2012-10-17'; policyDocument.Statement = []; var statementOne = {}; statementOne.Action = 'execute-api:Invoke'; statementOne.Effect = effect; statementOne.Resource = resource; policyDocument.Statement[0] = statementOne; authResponse.policyDocument = policyDocument; } // Optional output with custom properties of the String, Number or Boolean type. authResponse.context = { "stringKey": "stringval", "numberKey": 123, "booleanKey": true }; return authResponse; }
返回通配符“Resource”(资源)变量且基于请求参数的 Lambda 授权方函数代码示例
exports.handler = function(event, context, callback) { // Retrieve request parameters from the Lambda function input: var headers = event.headers; var queryStringParameters = event.queryStringParameters; var pathParameters = event.pathParameters; var stageVariables = event.stageVariables; // Parse the input for the parameter values var tmp = event.methodArn.split(':'); var apiGatewayArnTmp = tmp[5].split('/'); // Create wildcard resource var resource = tmp[0] + ":" + tmp[1] + ":" + tmp[2] + ":" + tmp[3] + ":" + tmp[4] + ":" + apiGatewayArnTmp[0] + '/*/*'; console.log("resource: " + resource); // if (apiGatewayArnTmp[3]) { // resource += apiGatewayArnTmp[3]; // } // Perform authorization to return the Allow policy for correct parameters and // the 'Unauthorized' error, otherwise. var authResponse = {}; var condition = {}; condition.IpAddress = {}; if (headers.headerauth1 === "headerValue1" && queryStringParameters.QueryString1 === "queryValue1" && stageVariables.StageVar1 === "stageValue1") { callback(null, generateAllow('me', resource)); } else { callback("Unauthorized"); } } // Help function to generate an IAM policy var generatePolicy = function(principalId, effect, resource) { // Required output: console.log("Resource in generatePolicy(): " + resource); var authResponse = {}; authResponse.principalId = principalId; if (effect && resource) { var policyDocument = {}; policyDocument.Version = '2012-10-17'; // default version policyDocument.Statement = []; var statementOne = {}; statementOne.Action = 'execute-api:Invoke'; // default action statementOne.Effect = effect; statementOne.Resource = resource; console.log("***Resource*** " + resource); policyDocument.Statement[0] = statementOne; console.log("***Generated Policy*** "); console.log(policyDocument); authResponse.policyDocument = policyDocument; } // Optional output with custom properties of the String, Number or Boolean type. authResponse.context = { "stringKey": "stringval", "numberKey": 123, "booleanKey": true }; return authResponse; } var generateAllow = function(principalId, resource) { return generatePolicy(principalId, 'Allow', resource); } var generateDeny = function(principalId, resource) { return generatePolicy(principalId, 'Deny', resource); }
有关如何编辑 Lambda 函数代码的更多信息,请参阅部署定义为 .zip 文件归档的 Lambda 函数。
相关信息
相关内容
- AWS 官方已更新 1 年前
- AWS 官方已更新 2 年前
- AWS 官方已更新 2 年前