为什么我的 API Gateway 代理资源使用已激活缓存的 Lambda 授权方返回 HTTP 403“User is not authorized to access this resource(用户无权访问此资源)”错误?

4 分钟阅读
0

我的 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 官方
AWS 官方已更新 2 年前