SAM 模板 - 使用 Lambda Authorizer 和简单响应定义 HttpApi

mas*_*azi 6 aws-cloudformation aws-lambda aws-api-gateway aws-sam

问题描述

我在 SAM 中使用 API Gateway 创建了一个 Lambda 函数,然后部署了它并且它按预期工作。在 API Gateway 中我使用了HttpApinot REST API

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

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

myappauth但是,当我添加带有 value 的标头时"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 函数的日志,那里没有任何内容(所有日志都是从我添加授权者之前开始的)。那么,我做错了什么?

我尝试过的:

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

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

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

    return response;

};
Run Code Online (Sandbox Code Playgroud)

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

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

Run Code Online (Sandbox Code Playgroud)

编辑:

用户 @petey 建议我尝试在授权者函数中返回 IAM 策略,因此我在 中更改EnableSimpleResponses为,然后按如下所示更改了我的函数,但得到了相同的结果:falsetemplate.yml

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;

};
Run Code Online (Sandbox Code Playgroud)

mas*_*azi 10

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

主要问题在于官方文档中提供的示例。他们有:

  MyLambdaRequestAuthorizer:
    FunctionArn: !GetAtt MyAuthFunction.Arn
    FunctionInvokeRole: !GetAtt MyAuthFunctionRole.Arn
Run Code Online (Sandbox Code Playgroud)

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

我在官方文档中错过的关键部分是这一段

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

以下命令授予 API Gateway 调用您的 Lambda 函数的权限。如果 API Gateway 无权调用您的函数,客户端会收到 500 内部服务器错误。

解决这个问题的最佳方法是在 SAM 中实际包含 Role 定义template.yml,如下Resources

MyAuthFunctionRole
  Type: AWS::IAM::Role
  Properties: 
    # [... other properties...]
    AssumeRolePolicyDocument:
       Version: "2012-10-17"
       Statement:
         - Effect: Allow
           Principal:
             Service:
               - apigateway.amazonaws.com
           Action:
             - 'sts:AssumeRole'
    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
Run Code Online (Sandbox Code Playgroud)

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

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

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

MyLambdaRequestAuthorizer:
    FunctionArn: !GetAtt MyAuthFunction.Arn
    FunctionInvokeRole: arn:aws:iam::[...]
Run Code Online (Sandbox Code Playgroud)