我的 API Gateway 代理資源具有已啟用快取的 Lambda 授權方,為何傳回 HTTP 403「使用者無權存取此資源」錯誤?

4 分的閱讀內容
0

我的 Amazon API Gateway 代理資源具有已啟用快取的 AWS Lambda 授權方,會傳回以下 HTTP 403 錯誤訊息:"User is not authorized to access this resource (使用者無權存取此資源)"。為什麼會發生這種情況?我該如何解決這個錯誤?

簡短描述

注意: API Gateway 可能會因多種原因而傳回 403 錯誤使用者無權存取此資源。本文解決了與具有已啟用快取的 Lambda 授權方的 API Gateway 代理資源相關的 403 錯誤。如需對其他類型的 403 錯誤進行疑難排解,請參閲如何針對 API Gateway 的 HTTP 403 錯誤進行疑難排解?

Lambda 授權方的輸出會將 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 授權方上啟用授權快取時,將快取傳回的 IAM 政策。然後,快取 IAM 政策將套用於在快取指定的存留時間 (TTL) 期間內發出的任何額外 API 請求。

如果 API 具有包含 {proxy+} 窮盡 (greedy) 路徑變數的代理資源,則第一個授權會成功。在快取 TTL 週期內對不同路徑發出的任何額外 API 請求均會失敗,並傳回以下錯誤:

"message":「使用者無權存取此資源」

額外請求失敗,因為路徑與快取的 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 函數


相關資訊

使用主控台編輯器編輯程式碼

AWS 官方
AWS 官方已更新 2 年前