為什麼我的 API Gateway Proxy 資源 (具有已啟動快取的 Lambda Authorizer) 會傳回 HTTP 403「使用者未獲得存取此資源的授權」錯誤?

4 分的閱讀內容
0

具有已啟動快取之 AWS Lambda Authorizer 的 Amazon API Gateway Proxy 資源會傳回下列 HTTP 403 錯誤訊息: 「使用者未獲得存取此資源的授權」。

簡短描述

**注意:**API Gateway 可能會因為各種原因而傳回 403 **「使用者未獲得存取此資源的授權」**錯誤。本文將解說與 API Gateway Proxy 資源 (具有僅啟用快取的 Lambda Authorizer) 相關的 403 錯誤。如需有關對其他 403 錯誤類型進行疑難排解的資訊,請參閱如何對 API Gateway 的 HTTP 403 錯誤進行疑難排解?

Lambda Authorizer 的輸出會將 AWS Identity and Access Management (IAM) 政策傳回 API Gateway。IAM 政策包含採用下列格式的明確 API Gateway API "Resource"元素

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

在 Lambda Authorizer 上啟用授權快取時,傳回的 IAM 政策會受到快取。然後,受到快取的 IAM 政策會套用至快取指定存留時間 (TTL) 期間內所發出的任何其他 API 請求。

如果 API 有一個具有窮盡路徑變數為 {proxy+} 的 Proxy 資源,則第一個授權將成功。在快取的 TTL 期間內對其他路徑發出的任何其他 API 請求都會失敗,並傳回下列錯誤:

「訊息」: 「使用者未獲得存取此資源的授權」

因為路徑不符合受到快取之 IAM 政策中定義的明確 API Gateway API "Resource" 元素,所以其他請求失敗。

若要解決此問題,您可以修改 Lambda Authorizer 的程式碼,改為在輸出中傳回萬用字元 (*/*) 資源。如需詳細資訊,請參閱 Lambda 動作的資源與條件

注意: 若要啟動 Authorizer 快取,您的 Authorizer 必須傳回適用於 API Gateway 中所有方法的政策。Lambda Authorizer 函數的程式碼必須在輸出中傳回萬用字元 (*/*) 資源,才能允許所有資源。除非您在相同的資源路徑上提出兩次相同的請求,否則快取政策預期會快取相同的資源路徑。

解決方法

**注意:**請修改本文中的範例 Lambda Authorizer 函數程式碼片段,以符合您的使用案例。

在下列範例設定中,Lambda 函數從方法的 Amazon Resource Name (ARN) ("event.methodArn") 中擷取 API Gateway 的 ID。然後,函數透過將方法 ARN 的路徑與 API 的 id 值和萬用字元 (*/*) 結合,以定義萬用字元 "Resource" 變數。

傳回萬用字元 "Resource" 變數的範例權杖型 Lambda Authorizer 函數程式碼

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 Authorizer 函數程式碼

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 函數