如何解决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;
}
,
我要回答我自己的问题,因为我已经解决了这个问题,我希望这能帮助那些打算在 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 举报,一经查实,本站将立刻删除。