【问题标题】:SAM Template - define HttpApi with Lambda Authorizer and Simple ResponseSAM 模板 - 使用 Lambda 授权器和简单响应定义 HttpApi
【发布时间】:2021-09-17 19:28:42
【问题描述】:

问题描述

我在 SAM 中使用 API Gateway 创建了一个 Lambda 函数,然后部署它,它按预期工作。在 API 网关中,我使用了 HttpApi 而不是 REST API

然后,我想添加一个带有简单响应的 Lambda 授权方。因此,我遵循了 SAM 和 API Gateway 文档,并提出了以下代码。

当我调用路由 items-list 时,它现在返回 401 Unauthorized,这是预期的。

但是,当我将标题 myappauth 添加到值 "test-token-abc" 时,我得到一个 500 Internal Server Error

我检查了这个页面,但似乎所有列出的步骤都可以https://aws.amazon.com/premiumsupport/knowledge-center/api-gateway-http-lambda-integrations/

我按照以下说明为 API 网关启用了日志记录:https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-logging.html

但我得到的只是这样的东西(编辑了我的 IP 和请求 ID):

[MY-IP] - - [07/Jul/2021:08:24:06 +0000] "GET GET /items-list/{userNumber} HTTP/1.1" 500 35 [REQUEST-ID]

(也许我可以配置记录器,使其打印更有意义的错误消息?编辑:我尝试将$context.authorizer.error 添加到日志中,但它不打印任何特定的错误消息,只是打印破折号:-)

我还检查了 Lambda 函数的日志,那里什么都没有(所有日志都来自我添加授权人之前的时间)。 那么,我做错了什么?

我尝试了什么:

这是我使用 sam deploy 部署的 Lambda Authorizer 函数,当我使用带有 myappauth 标头的 event 单独测试它时,它可以工作:

exports.authorizer = async (event) => {
    let response = {
        "isAuthorized": false,
    };

    if (event.headers.myappauth === "test-token-abc") {
        response = {
            "isAuthorized": true,
        };
    }

    return response;

};

这是我使用 sam deploy 部署的 SAM template.yml

AWSTemplateFormatVersion: 2010-09-09
Description: >-
  myapp-v1

Transform:
  - AWS::Serverless-2016-10-31

Globals:
  Function:
    Runtime: nodejs14.x
    MemorySize: 128
    Timeout: 100
    Environment:
      Variables:
        MYAPP_TOKEN: "test-token-abc"

Resources:
  MyAppAPi:
    Type: AWS::Serverless::HttpApi
    Properties:
      FailOnWarnings: true
      Auth:
        Authorizers:
          MyAppLambdaAuthorizer:
            AuthorizerPayloadFormatVersion: "2.0"
            EnableSimpleResponses: true
            FunctionArn: !GetAtt authorizerFunction.Arn
            FunctionInvokeRole: !GetAtt authorizerFunctionRole.Arn
            Identity:
              Headers:
                - myappauth
        DefaultAuthorizer: MyAppLambdaAuthorizer

  itemsListFunction:
    Type: AWS::Serverless::Function
    Properties:
      Handler: src/handlers/v1-handlers.itemsList
      Description: A Lambda function that returns a list of items.
      Policies:
        - AWSLambdaBasicExecutionRole
      Events:
        Api:
          Type: HttpApi
          Properties:
            Path: /items-list/{userNumber}
            Method: get
            ApiId: MyAppAPi

  authorizerFunction:
    Type: AWS::Serverless::Function
    Properties:
      Handler: src/handlers/v1-handlers.authorizer
      Description: A Lambda function that authorizes requests.
      Policies:
        - AWSLambdaBasicExecutionRole

编辑:

用户 @petey 建议我尝试在我的授权函数中返回一个 IAM 策略,所以我在 template.yml 中将 EnableSimpleResponses 更改为 false,然后我将函数更改如下,但得到了相同的结果:

exports.authorizer = async (event) => {
    let response = {
        "principalId": "my-user",
        "policyDocument": {
            "Version": "2012-10-17",
            "Statement": [{
                "Action": "execute-api:Invoke",
                "Effect": "Deny",
                "Resource": event.routeArn
            }]
        }
    };

    if (event.headers.myappauth == "test-token-abc") {
        response = {
            "principalId": "my-user",
            "policyDocument": {
                "Version": "2012-10-17",
                "Statement": [{
                    "Action": "execute-api:Invoke",
                    "Effect": "Allow",
                    "Resource": event.routeArn
                }]
            }
        };
    }

    return response;

};

【问题讨论】:

    标签: aws-lambda amazon-cloudformation aws-api-gateway aws-sam


    【解决方案1】:

    您的 lambda 授权方未返回预期的实际 lambda 授权方(IAM 策略)。这可以解释内部错误 500。

    要解决此问题,请使用返回 IAM 策略(或拒绝)的类似内容替换:

    // A simple token-based authorizer example to demonstrate how to use an authorization token 
    // to allow or deny a request. In this example, the caller named 'user' is allowed to invoke 
    // a request if the client-supplied token value is 'allow'. The caller is not allowed to invoke 
    // the request if the token value is 'deny'. If the token value is 'unauthorized' or an empty
    // string, the authorizer function returns an HTTP 401 status code. For any other token value, 
    // the authorizer returns an HTTP 500 status code. 
    // Note that token values are case-sensitive.
    
    exports.handler =  function(event, context, callback) {
        var token = event.authorizationToken;
        // modify switch statement here to your needs
        switch (token) {
            case 'allow':
                callback(null, generatePolicy('user', 'Allow', event.methodArn));
                break;
            case 'deny':
                callback(null, generatePolicy('user', 'Deny', event.methodArn));
                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;
    }
    

    这里有更多信息:https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-use-lambda-authorizer.html#api-gateway-lambda-authorizer-lambda-function-create

    【讨论】:

    • 您的示例适用于 REST API,但我使用的是描述为 here 的 HTTP API。我正在使用简单响应,因此不需要 IAM 策略。无论如何,我尝试将 EnableSimpleResponses 设置为 false,然后返回 IAM 策略,采用 HTTP API 预期的格式,如here 所述,但不幸的是我仍然得到相同的结果。我更新了答案以反映这一点。
    【解决方案2】:

    我将回答我自己的问题,因为我已经解决了这个问题,我希望这将帮助那些打算在 API Gateway 中使用新的“HTTP API”格式的人,因为没有很多教程出来还有;您可以在网上找到的大多数示例都是针对较旧的 API Gateway 标准,亚马逊将其称为“REST API”。 (如果你想知道两者的区别,see here)。

    主要问题在于example that is presented in the official documentation。他们有:

      MyLambdaRequestAuthorizer:
        FunctionArn: !GetAtt MyAuthFunction.Arn
        FunctionInvokeRole: !GetAtt MyAuthFunctionRole.Arn
    

    问题在于,此模板将创建一个名为 MyAuthFunctionRole 的新角色,但该角色不会附加所有必要的策略!

    我在官方文档中遗漏的关键部分是this paragraph

    您必须授予 API Gateway 权限以使用函数的资源策略或 IAM 角色调用 Lambda 函数。对于此示例,我们更新了函数的资源策略,以便它授予 API Gateway 调用我们的 Lambda 函数的权限。

    以下命令授予 API Gateway 调用您的 Lambda 函数的权限。如果 API Gateway 无权调用您的函数,客户端会收到 500 Internal Server Error。

    解决此问题的最佳方法是将角色定义实际包含在 SAM template.yml 中的 Resources 下:

    MyAuthFunctionRole
      Type: AWS::IAM::Role
      Properties: 
        # [... other properties...]
        Policies: 
          # here you will put the InvokeFunction policy, for example:
          - PolicyName: MyPolicy
            PolicyDocument:
              Version: "2012-10-17"
              Statement:
                - Effect: Allow
                  Action: 'lambda:InvokeFunction'
                  Resource: !GetAtt MyAuthFunction.Arn
    

    您可以在此处查看有关角色的各种属性的说明:https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-iam-role.html

    解决此问题的另一种方法是在 AWS 控制台中单独创建一个具有 InvokeFunction 权限的新策略,然后在部署后将该策略附加到 SAM 创建的 MyAuthFunctionRole。现在 Authorizer 将按预期工作。

    另一种策略是预先创建一个新角色,该角色具有具有InvokeFunction 权限的策略,然后将该角色的arn 复制并粘贴到SAM template.yml 中:

    MyLambdaRequestAuthorizer:
        FunctionArn: !GetAtt MyAuthFunction.Arn
        FunctionInvokeRole: arn:aws:iam::[...]
    

    【讨论】:

    • 您为自己的角色附加了哪些政策?
    • 您需要使用InvokeFunction 创建策略,请参阅此处以获取示例docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/…,但由于您可能想要访问所有功能,因此在Resource 中您应该只添加一个星号:@987654339 @
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-01-16
    • 2021-12-09
    • 1970-01-01
    • 1970-01-01
    • 2018-11-21
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多