微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

SAM 模板 - 使用 Lambda 授权器和简单响应定义 HttpApi

如何解决SAM 模板 - 使用 Lambda 授权器和简单响应定义 HttpApi

问题描述

我在 SAM 中创建了一个带有 API Gateway 的 Lambda 函数,然后部署了它并且它按预期工作。在 API Gateway 中,我使用了 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 函数,当我使用带有 event 标头的 myappauth 对其进行单独测试时,它可以工作:

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

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

    return response;

};

这是我使用 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

编辑:

用户@petey 建议我尝试在我的授权方函数中返回 IAM 策略,因此我将 EnableSimpleResponses 中的 false 更改为 template.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;

};

解决方法

您的 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,'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

,

我要回答我自己的问题,因为我已经解决了这个问题,我希望这能帮助那些打算在 API Gateway 中使用新的“HTTP API”格式的人,因为没有很多教程还有;您可以在网上找到的大多数示例都是针对较旧的 API 网关标准,亚马逊将其称为“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 内部服务器错误。

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

MyAuthFunctionRole
  Type: AWS::IAM::Role
  Properties: 
    # [... other properties...]
    Policies: 
      # here you will put the InvokeFunction policy

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

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

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

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

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。