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

4 分钟阅读
0

使用已激活缓存的 AWS Lambda 授权方的 Amazon API Gateway 代理资源返回以下 HTTP 403 错误消息: “User is not authorized to access this resource”(用户无权访问此资源)。

简短描述

**注意:**API Gateway 可能会因为各种原因返回 403“用户无权访问此资源”错误。本文仅解决与使用已激活缓存的 Lambda 授权方的 API Gateway 代理资源相关的 403 错误。有关其他类型的 403 错误的故障排除信息,请参阅如何对 API Gateway 中的 HTTP 403 错误进行故障排除?

Lambda 授权方的输出会向 API Gateway 返回 AWS Identity and Access Management (IAM) 策略。该 IAM 策略将包含显式的 API Gateway API“Resource”元素,其格式如下:

"arn:aws:execute-api:<region>:<account>:<API_id>/<stage>/<http-method>/[<resource-path-name>/[<child-resources-path>]"

如果在 Lambda 授权方上激活授权缓存,则返回的 IAM 策略将被缓存。然后,缓存的 IAM 策略将应用于在缓存的指定生存时间 (TTL) 周期内提出的任何其他 API 请求。

如果 API 的代理资源的贪婪路径变量为 {proxy+},则第一次授权将成功。在缓存的 TTL 周期内向其他路径发出的任何其他 API 请求都会失败并返回以下错误:

"message": “User is not authorized to access this resource”(用户无权访问此资源)

其他的请求失败是因为路径与缓存的 IAM 策略中定义的显式 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 函数

相关信息

在控制台中测试 Lambda 函数

AWS 官方
AWS 官方已更新 7 个月前