我正在尝试找到一种方法来允许 AWS 中的自定义授权者使用访客模式。
基本上,我想要实现的是以下场景:
Authorization
标头,则触发 lambda 函数来响应一些数据Authorization
标头,那么我的自定义授权者应检查 JWT 令牌以及 Allow
或 Deny
。如果 custom-authorizer
返回 Allow
我发现我可以实现其中一个,但不能同时实现两者,即我可以打开端点(完全删除
authorizer
),这可以正常工作,或者我可以放置 authorizer
,这又可以正常工作。
但是,当没有
custom-authorizer
标头时,我没有找到绕过 Authorization
的方法。
无服务器中的示例配置:
functions:
hello:
handler: handler.hello
events:
- http:
path: /hello
method: get
private: true
authorizer:
identitySource: method.request.header.Authorization # Can this be optional?
name: custom-authorizer
custom-authorizer:
handler: authorizer.handler
从我的测试中,我可以确认,一旦
authorization
存在并且请求中没有 Authorization
标头,那么我的 custom-authorizer
根本不会被触发,并且 API 网关会立即响应 401 Unauthorized
。
请考虑到,在我的访客模式下,我不想想要获得自定义 API 网关响应(这是可能的并且有效)。我想触发 lambda 函数,就像根本没有授权一样。
我得出的结论是这是不可能的,唯一的解决方法是删除
authorization
,然后在 lambda 中执行一些自定义代码。
有什么建议吗?
如果您将 sls 事件处理程序 配置为
httpApi
*,而不是 http
,则 identitySource
是 authorizer
的可选输入(假设无服务器 api 与 AWS api 匹配)。
相关的 CloudFormation
AWS::ApiGatewayV2::Authorizer
文档位于此处。
正如您所经历的,如果
identitySource
已配置但未出现在客户端请求中,Apig 将返回 401
。如果您在配置中省略 identitySource
,则所有请求都将发送到您的授权者 lambda。然后,您的 lambda 可以处理三种情况:无令牌、坏令牌、好令牌。
*
httpApi
在 AWS CloudFormation 中部署为 ApiGatewayV2
。正如 Serverless 文档所说,V2“比 v1 更快、更便宜”。 “HTTP Api”(V2)“Rest Api”(V1) 之间的 api 和 功能集 有所不同,但 httpApi
是一个不错的默认选择。
跟进@fedonev 的回答是完整的解决方案:
HTTP API
代替 REST API
。custom-authorizer
让我们开始根据较新且
不同的HTTP API 语法更新
serverless.yml
:
将
events.http
替换为 events.httpApi
,并将 httpApi.authorizer
添加到 provider
。更新了serverless.yml
:
provider:
httpApi: # Added
authorizers:
customAuthorizer:
type: request
functionName: custom-authorizer
functions:
hello:
handler: handler.hello
events:
- httpApi: # Changed
path: /hello
method: get
authorizer:
name: customAuthorizer # Changed
custom-authorizer:
handler: authorizer.handler
接下来,我们还需要更新授权处理程序函数。它仍然需要像使用 REST API 一样返回策略。然而......不再有
event.authorizationToken
和event.methodArn
了(我把它注释掉以供参考):
module.exports.handler = async (event) => {
if (
!event.headers.authorization ||
event.headers.authorization === 'Bearer ABCDEF'
) {
// if (event.authorizationToken === 'Bearer ABCDEF')
return {
principalId: 'anonymous',
policyDocument: {
Version: '2012-10-17',
Statement: [
{
Action: 'execute-api:Invoke',
Effect: 'Allow',
Resource: event.routeArn,
// Resource: event.methodArn,
},
],
},
};
}
throw Error('Unauthorized');
};
现在,我们可以看到我们对授权有了完全的控制,我们可以检查令牌是否存在:
!event.headers.authorization
(这模拟访客模式)或者令牌是否有效:event.headers.authorization === 'Bearer ABCDEF'
。如果令牌无效,我们会抛出错误,并按预期给出 401 Unauthorized
。
最后,值得注意的是,所有
headers
键都是小写的,并且 principalId
是强制性的(与之前一样,当使用 REST API 时,授权者无需使用它即可工作)。
部署并享受!
此处未提及的一个解决方案是,您可以选择发送带有未经身份验证的请求的自定义授权标头(即
Authentication: "UNAUTHENTICATED"
),然后将该特定标头的请求评估为未经身份验证。
这需要与前端协调,但可以避开 401,并允许您仍然将上下文传递到下游 API。