无服务器框架中的共享 Lambda 授权方设置
Shared Lambda authorizer setup in Serverless Framework
我正在尝试创建一个自定义 Lambda 授权方,它将在几个不同的 services/serverless 堆栈之间共享。如果我理解此处 https://serverless.com/framework/docs/providers/aws/events/apigateway/#note-while-using-authorizers-with-shared-api-gateway 的文档,则意味着我需要在“公共资源”service/serverless 堆栈中创建一个共享授权方资源,然后从我的其他服务中引用该共享授权方。首先:我的理解对吗?
如果我的理解是正确的,那么我的下一个问题就是:我该怎么做?该文档没有为 lambda 授权者提供明确的示例,因此这是我尝试自定义它的方式:
functions:
authorizerFunc:
handler: authorizer/authorizer.handler
runtime: nodejs8.10
resources:
Resources:
authorizer:
Type: AWS::ApiGateway::Authorizer
Properties:
AuthorizerResultTtlInSeconds: 0
Name: Authorizer
Type: REQUEST
AuthorizerUri: ???
RestApiId:
Fn::ImportValue: myRestApiId
我不明白 AuthorizerUri 的语法应该是什么。我已经尝试过“Ref: authorizerFunc”、“Fn::GetAtt: [authorizerFunc, Arn]”等,但都无济于事。
当我让 authorizerUri 工作时,我是否只为我的授权资源添加一个输出,然后 Fn::ImportValue 它来自包含我的 API Lambdas 的服务?
Link 我在无服务器论坛上为后代提出的问题:https://forum.serverless.com/t/shared-lambda-authorizer/6447
我遇到了与您描述的相同的问题。或者至少我是这么认为的。我按照您提供的链接上的文档设法解决了这个问题。
无服务器文档声明授权方格式为
authorizer:
# Provide both type and authorizerId
type: COGNITO_USER_POOLS # TOKEN or COGNITO_USER_POOLS, same as AWS Cloudformation documentation
authorizerId:
Ref: ApiGatewayAuthorizer # or hard-code Authorizer ID
据我了解,我的解决方案(在下面提供)遵循硬编码授权方 ID 方法。
在具有共享授权者的服务中,它以正常方式在serverless.yml中声明,即
functions:
myCustomAuthorizer:
handler: path/to/authorizer.handler
name: my-shared-custom-authorizer
然后在希望使用这个共享授权器的服务中,servlerless.yml中的函数被声明为
functions:
foo:
# some properties ...
events:
- http:
# ... other properties ...
authorizer:
name: authorize
arn:
Fn::Join:
- ""
- - "arn:aws:lambda"
# References to values such as region, account id, stage, etc
# Can be done with Pseudo Parameter Reference
- ":"
- "function:myCustomAuthorizer"
添加名称 属性 至关重要。没有它就不行,至少目前是这样。
详情见
遗憾的是,与您将授权方定义为资源的建议相比,我不能说这种方法是否有一些局限性。事实上,这可能会使在同一服务的多个功能中重复使用同一授权方变得更加容易。
编辑:显然我的回答现在已经过时了。对于较新版本的无服务器,请参阅其他答案。我不知道哪个答案是 best/most 最新的,但如果有人告诉我,我会更改接受哪个答案。
我最终让它工作了,下面是我如何设置我的 autherizer serverless.yml:
service: user-admin-authorizer
custom:
region: ${file(serverless.env.yml):${opt:stage}.REGION}
provider:
name: aws
region: ${self:custom.region}
functions:
authorizer:
handler: src/authorizer.handler
runtime: nodejs8.10
resources:
Resources:
Authorizer:
Type: AWS::ApiGateway::Authorizer
Properties:
Name: Authorizer
Type: REQUEST
AuthorizerUri:
Fn::Join: [ "",
[
"arn:aws:apigateway:",
"${self:custom.region}",
":lambda:path/",
"2015-03-31/functions/",
Fn::GetAtt: ["AuthorizerLambdaFunction", "Arn" ],
"/invocations"
]]
RestApiId:
Fn::ImportValue: api-gateway:${opt:stage}:rest-api-id
apiGatewayLambdaPermissions:
Type: AWS::Lambda::Permission
Properties:
FunctionName:
Fn::GetAtt: [ AuthorizerLambdaFunction, Arn]
Action: lambda:InvokeFunction
Principal:
Fn::Join: [ "",
[
"apigateway.",
Ref: AWS::URLSuffix
]]
Outputs:
AuthorizerRef:
Value:
Ref: Authorizer
Export:
Name: authorizer-ref:${opt:stage}
注意事项:授权函数虽然叫“authorizer”,但在GetAtt中使用时需要首字母大写,并在名字后面加上“LambdaFunction”,所以“authorizer”变成了“AuthorizerLambdaFunction”一些理由。我还必须添加 lambda 权限资源。
API 网关资源也需要两个输出,它的 API ID 和它的 API 根资源 ID。以下是我的 API 网关 serverless.yml 的设置方式:
resources:
Resources:
ApiGateway:
Type: AWS::ApiGateway::RestApi
Properties:
Name: ApiGateway
Outputs:
ApiGatewayRestApiId:
Value:
Ref: ApiGateway
Export:
Name: api-gateway:${opt:stage}:rest-api-id
ApiGatewayRestApiRootResourceId:
Value:
Fn::GetAtt:
- ApiGateway
- RootResourceId
Export:
Name: api-gateway:${opt:stage}:root-resource-id
现在您只需向您的其他服务指定它们应该使用此 API 网关(导入的值是 API 网关的输出):
provider:
name: aws
apiGateway:
restApiId:
Fn::ImportValue: api-gateway:${opt:stage}:rest-api-id
restApiRootResourceId:
Fn::ImportValue: api-gateway:${opt:stage}:root-resource-id
之后,可以像这样将授权方添加到此服务中的各个功能:
authorizer:
type: CUSTOM
authorizerId:
Fn::ImportValue: authorizer-ref:${opt:stage}
无服务器 1.35.1
对于遇到这个问题的人来说,这是新方法
无论您在何处创建用户池,都可以继续添加 ApiGatewayAuthorizer
# create a user pool as normal
CognitoUserPoolClient:
Type: AWS::Cognito::UserPoolClient
Properties:
# Generate an app client name based on the stage
ClientName: ${self:custom.stage}-user-pool-client
UserPoolId:
Ref: CognitoUserPool
ExplicitAuthFlows:
- ADMIN_NO_SRP_AUTH
GenerateSecret: true
# then add an authorizer you can reference later
ApiGatewayAuthorizer:
DependsOn:
# this is pre-defined by serverless
- ApiGatewayRestApi
Type: AWS::ApiGateway::Authorizer
Properties:
Name: cognito_auth
# apparently ApiGatewayRestApi is a global string
RestApiId: { "Ref" : "ApiGatewayRestApi" }
IdentitySource: method.request.header.Authorization
Type: COGNITO_USER_POOLS
ProviderARNs:
- Fn::GetAtt: [CognitoUserPool, Arn]
然后当你定义你的函数时
graphql:
handler: src/app.graphqlHandler
events:
- http:
path: /
method: post
cors: true
integration: lambda
# add this and just reference the authorizer
authorizer:
type: COGNITO_USER_POOLS
authorizerId:
Ref: ApiGatewayAuthorizer
这就是我进行设置的方式,因为上面发布的答案对我不起作用。可能对某人有帮助。
resources:
Resources:
ApiGatewayAuthorizer:
Type: AWS::ApiGateway::Authorizer
Properties:
AuthorizerResultTtlInSeconds: 0
IdentitySource: method.request.header.Authorization
AuthorizerUri:
Fn::Join: ["",
[
"arn:aws:apigateway:",
"${self:custom.region}",
":lambda:path/",
"2015-03-31/functions/",
Fn::GetAtt: ["YourFunctionNameLambdaFunction", "Arn" ],
"/invocations"
]]
RestApiId:
Fn::ImportValue: ${self:custom.stage}-ApiGatewayRestApiId
Name: api-${self:custom.stage}-authorizer
Type: REQUEST
ApiGatewayAuthorizerPermission:
Type: AWS::Lambda::Permission
Properties:
FunctionName:
Fn::GetAtt: ["YourFunctionNameLambdaFunction", "Arn"]
Action: lambda:InvokeFunction
Principal:
Fn::Join: ["",["apigateway.", { Ref: "AWS::URLSuffix"}]]
Outputs:
AuthorizerRef:
Value:
Ref: ApiGatewayAuthorizer
Export:
Name: authorizer-ref:${self:custom.stage}
我希望你知道如何添加 API 网关并像
一样在此处导入它
RestApiId:
Fn::ImportValue: ${self:custom.stage}-ApiGatewayRestApiId
,因为它已在接受的答案中指定
And In my case I passed value in the event header as Authorization
type to get it in the authorizer lambda function and my type is
REQUEST
更改为共享自定义 API Gateway Lambda Authorizer 一旦它作为服务的一部分工作就很简单。那时它只是添加一个 arn: 到已部署的 lambda(授权方)并将 "authorizer" 定义从服务中删除到单独的可部署服务。
myLambdaName:
handler: handler.someNodeFunction
name: something-someNodeFunction
events:
- http:
path: /path/to/resource
method: get
cors: true
authorizer:
name: myCustomAuthorizer
# forwarding lambda proxy event stuff to the custom authorizer
IdentitySource: method.request.header.Authorization, context.path
type: request
arn: 'arn:aws:lambda:region:##:function:something-else-myCustomAuthorizer'
然后另一个 "service" 只是有一些由多个部署的微服务共享的自定义授权者。
functions:
myCustomAuthorizer:
name: something-else-myCustomAuthorizer
handler: handler.myCustomAuthorizer
旁注:
如果您想要令牌类型的授权方,它将 "authorization: bearer xyzsddfsf" 作为简单事件转发:
{
"type": "TOKEN",
"methodArn": "arn:aws:execute-api:region:####:apigwIdHere/dev/GET/path/to/resource",
"authorizationToken": "Bearer ...."
}
authorizer:
arn: 'arn:aws:lambda:region:##:function:something-else-myCustomAuthorizer'
我正在尝试创建一个自定义 Lambda 授权方,它将在几个不同的 services/serverless 堆栈之间共享。如果我理解此处 https://serverless.com/framework/docs/providers/aws/events/apigateway/#note-while-using-authorizers-with-shared-api-gateway 的文档,则意味着我需要在“公共资源”service/serverless 堆栈中创建一个共享授权方资源,然后从我的其他服务中引用该共享授权方。首先:我的理解对吗?
如果我的理解是正确的,那么我的下一个问题就是:我该怎么做?该文档没有为 lambda 授权者提供明确的示例,因此这是我尝试自定义它的方式:
functions:
authorizerFunc:
handler: authorizer/authorizer.handler
runtime: nodejs8.10
resources:
Resources:
authorizer:
Type: AWS::ApiGateway::Authorizer
Properties:
AuthorizerResultTtlInSeconds: 0
Name: Authorizer
Type: REQUEST
AuthorizerUri: ???
RestApiId:
Fn::ImportValue: myRestApiId
我不明白 AuthorizerUri 的语法应该是什么。我已经尝试过“Ref: authorizerFunc”、“Fn::GetAtt: [authorizerFunc, Arn]”等,但都无济于事。
当我让 authorizerUri 工作时,我是否只为我的授权资源添加一个输出,然后 Fn::ImportValue 它来自包含我的 API Lambdas 的服务?
Link 我在无服务器论坛上为后代提出的问题:https://forum.serverless.com/t/shared-lambda-authorizer/6447
我遇到了与您描述的相同的问题。或者至少我是这么认为的。我按照您提供的链接上的文档设法解决了这个问题。
无服务器文档声明授权方格式为
authorizer:
# Provide both type and authorizerId
type: COGNITO_USER_POOLS # TOKEN or COGNITO_USER_POOLS, same as AWS Cloudformation documentation
authorizerId:
Ref: ApiGatewayAuthorizer # or hard-code Authorizer ID
据我了解,我的解决方案(在下面提供)遵循硬编码授权方 ID 方法。
在具有共享授权者的服务中,它以正常方式在serverless.yml中声明,即
functions:
myCustomAuthorizer:
handler: path/to/authorizer.handler
name: my-shared-custom-authorizer
然后在希望使用这个共享授权器的服务中,servlerless.yml中的函数被声明为
functions:
foo:
# some properties ...
events:
- http:
# ... other properties ...
authorizer:
name: authorize
arn:
Fn::Join:
- ""
- - "arn:aws:lambda"
# References to values such as region, account id, stage, etc
# Can be done with Pseudo Parameter Reference
- ":"
- "function:myCustomAuthorizer"
添加名称 属性 至关重要。没有它就不行,至少目前是这样。
详情见
遗憾的是,与您将授权方定义为资源的建议相比,我不能说这种方法是否有一些局限性。事实上,这可能会使在同一服务的多个功能中重复使用同一授权方变得更加容易。
编辑:显然我的回答现在已经过时了。对于较新版本的无服务器,请参阅其他答案。我不知道哪个答案是 best/most 最新的,但如果有人告诉我,我会更改接受哪个答案。
我最终让它工作了,下面是我如何设置我的 autherizer serverless.yml:
service: user-admin-authorizer
custom:
region: ${file(serverless.env.yml):${opt:stage}.REGION}
provider:
name: aws
region: ${self:custom.region}
functions:
authorizer:
handler: src/authorizer.handler
runtime: nodejs8.10
resources:
Resources:
Authorizer:
Type: AWS::ApiGateway::Authorizer
Properties:
Name: Authorizer
Type: REQUEST
AuthorizerUri:
Fn::Join: [ "",
[
"arn:aws:apigateway:",
"${self:custom.region}",
":lambda:path/",
"2015-03-31/functions/",
Fn::GetAtt: ["AuthorizerLambdaFunction", "Arn" ],
"/invocations"
]]
RestApiId:
Fn::ImportValue: api-gateway:${opt:stage}:rest-api-id
apiGatewayLambdaPermissions:
Type: AWS::Lambda::Permission
Properties:
FunctionName:
Fn::GetAtt: [ AuthorizerLambdaFunction, Arn]
Action: lambda:InvokeFunction
Principal:
Fn::Join: [ "",
[
"apigateway.",
Ref: AWS::URLSuffix
]]
Outputs:
AuthorizerRef:
Value:
Ref: Authorizer
Export:
Name: authorizer-ref:${opt:stage}
注意事项:授权函数虽然叫“authorizer”,但在GetAtt中使用时需要首字母大写,并在名字后面加上“LambdaFunction”,所以“authorizer”变成了“AuthorizerLambdaFunction”一些理由。我还必须添加 lambda 权限资源。
API 网关资源也需要两个输出,它的 API ID 和它的 API 根资源 ID。以下是我的 API 网关 serverless.yml 的设置方式:
resources:
Resources:
ApiGateway:
Type: AWS::ApiGateway::RestApi
Properties:
Name: ApiGateway
Outputs:
ApiGatewayRestApiId:
Value:
Ref: ApiGateway
Export:
Name: api-gateway:${opt:stage}:rest-api-id
ApiGatewayRestApiRootResourceId:
Value:
Fn::GetAtt:
- ApiGateway
- RootResourceId
Export:
Name: api-gateway:${opt:stage}:root-resource-id
现在您只需向您的其他服务指定它们应该使用此 API 网关(导入的值是 API 网关的输出):
provider:
name: aws
apiGateway:
restApiId:
Fn::ImportValue: api-gateway:${opt:stage}:rest-api-id
restApiRootResourceId:
Fn::ImportValue: api-gateway:${opt:stage}:root-resource-id
之后,可以像这样将授权方添加到此服务中的各个功能:
authorizer:
type: CUSTOM
authorizerId:
Fn::ImportValue: authorizer-ref:${opt:stage}
无服务器 1.35.1 对于遇到这个问题的人来说,这是新方法
无论您在何处创建用户池,都可以继续添加 ApiGatewayAuthorizer
# create a user pool as normal
CognitoUserPoolClient:
Type: AWS::Cognito::UserPoolClient
Properties:
# Generate an app client name based on the stage
ClientName: ${self:custom.stage}-user-pool-client
UserPoolId:
Ref: CognitoUserPool
ExplicitAuthFlows:
- ADMIN_NO_SRP_AUTH
GenerateSecret: true
# then add an authorizer you can reference later
ApiGatewayAuthorizer:
DependsOn:
# this is pre-defined by serverless
- ApiGatewayRestApi
Type: AWS::ApiGateway::Authorizer
Properties:
Name: cognito_auth
# apparently ApiGatewayRestApi is a global string
RestApiId: { "Ref" : "ApiGatewayRestApi" }
IdentitySource: method.request.header.Authorization
Type: COGNITO_USER_POOLS
ProviderARNs:
- Fn::GetAtt: [CognitoUserPool, Arn]
然后当你定义你的函数时
graphql:
handler: src/app.graphqlHandler
events:
- http:
path: /
method: post
cors: true
integration: lambda
# add this and just reference the authorizer
authorizer:
type: COGNITO_USER_POOLS
authorizerId:
Ref: ApiGatewayAuthorizer
这就是我进行设置的方式,因为上面发布的答案对我不起作用。可能对某人有帮助。
resources:
Resources:
ApiGatewayAuthorizer:
Type: AWS::ApiGateway::Authorizer
Properties:
AuthorizerResultTtlInSeconds: 0
IdentitySource: method.request.header.Authorization
AuthorizerUri:
Fn::Join: ["",
[
"arn:aws:apigateway:",
"${self:custom.region}",
":lambda:path/",
"2015-03-31/functions/",
Fn::GetAtt: ["YourFunctionNameLambdaFunction", "Arn" ],
"/invocations"
]]
RestApiId:
Fn::ImportValue: ${self:custom.stage}-ApiGatewayRestApiId
Name: api-${self:custom.stage}-authorizer
Type: REQUEST
ApiGatewayAuthorizerPermission:
Type: AWS::Lambda::Permission
Properties:
FunctionName:
Fn::GetAtt: ["YourFunctionNameLambdaFunction", "Arn"]
Action: lambda:InvokeFunction
Principal:
Fn::Join: ["",["apigateway.", { Ref: "AWS::URLSuffix"}]]
Outputs:
AuthorizerRef:
Value:
Ref: ApiGatewayAuthorizer
Export:
Name: authorizer-ref:${self:custom.stage}
我希望你知道如何添加 API 网关并像
一样在此处导入它RestApiId:
Fn::ImportValue: ${self:custom.stage}-ApiGatewayRestApiId
,因为它已在接受的答案中指定
And In my case I passed value in the event header as Authorization type to get it in the authorizer lambda function and my type is REQUEST
更改为共享自定义 API Gateway Lambda Authorizer 一旦它作为服务的一部分工作就很简单。那时它只是添加一个 arn: 到已部署的 lambda(授权方)并将 "authorizer" 定义从服务中删除到单独的可部署服务。
myLambdaName:
handler: handler.someNodeFunction
name: something-someNodeFunction
events:
- http:
path: /path/to/resource
method: get
cors: true
authorizer:
name: myCustomAuthorizer
# forwarding lambda proxy event stuff to the custom authorizer
IdentitySource: method.request.header.Authorization, context.path
type: request
arn: 'arn:aws:lambda:region:##:function:something-else-myCustomAuthorizer'
然后另一个 "service" 只是有一些由多个部署的微服务共享的自定义授权者。
functions:
myCustomAuthorizer:
name: something-else-myCustomAuthorizer
handler: handler.myCustomAuthorizer
旁注:
如果您想要令牌类型的授权方,它将 "authorization: bearer xyzsddfsf" 作为简单事件转发:
{
"type": "TOKEN",
"methodArn": "arn:aws:execute-api:region:####:apigwIdHere/dev/GET/path/to/resource",
"authorizationToken": "Bearer ...."
}
authorizer:
arn: 'arn:aws:lambda:region:##:function:something-else-myCustomAuthorizer'