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



问题描述

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

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

当我调用路由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:部署的SAMtemplate.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建议我尝试在authorizer函数中返回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;
};

我将回答我自己的问题,因为我已经解决了这个问题,我希望这将帮助那些将要使用新的"HTTP API";API网关中的格式,因为目前还没有很多教程;你会在网上找到的大多数例子是旧的API网关标准,亚马逊称之为;REST API";。(如果您想知道两者之间的区别,请参阅此处(。

主要问题在于官方文件中的例子。他们有:

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

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

我在官方文件中遗漏的关键部分是这一段:

您必须授予API网关权限,才能使用函数的资源策略或IAM角色调用Lambda函数。对于本例,我们更新函数的资源策略,以便它授予API网关调用Lambda函数的权限。

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

解决此问题的最佳方法是在SAMtemplate.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

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

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

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

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

只是为了完成答案。您必须在"属性"下添加一个"假定角色策略文档"。

然后角色将声明

MyAuthFunctionRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service:
- apigateway.amazonaws.com
Action:
- 'sts:AssumeRole'
Policies:
# see answer above

您的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-网关lambda授权器lambda函数创建

相关内容

  • 没有找到相关文章

最新更新