Настройка общего лямбда-авторизатора в Serverless Framework
Я пытаюсь создать собственный Lambda-авторизатор, который будет разделен между несколькими различными стеками служб / серверов. Если я понимаю документацию здесь https://serverless.com/framework/docs/providers/aws/events/apigateway/, это означает, что мне нужно создать ресурс общего авторизатора в службе / стеке "общих ресурсов", а затем обратитесь к этому общему авторизатору из других моих служб. Прежде всего: правильно ли мое понимание?
Если мое понимание верно, мой следующий вопрос звучит так: как мне это сделать? Документация не дает четкого примера для лямбда-авторизаторов, поэтому вот как я пытался ее настроить:
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 из сервисов, содержащих мой Lambdas API?
Ссылка на мой вопрос на форуме о сервере для потомков: https://forum.serverless.com/t/shared-lambda-authorizer/6447
0 ответов
В конце концов я заставил его работать, вот как я настроил 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 называется "Authorizer", вам необходимо использовать заглавную букву и добавлять "LambdaFunction" к ее имени при использовании его с GetAtt, поэтому "Authorizer" становится "AuthorizerLambdaFunction" по некоторым причинам. Я также должен был добавить ресурс разрешения лямбды.
Ресурсу шлюза API также требуется два выхода: его идентификатор API и идентификатор корневого ресурса API. Вот как настроен serverless.yml моего шлюза API:
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}
У меня была та же проблема, которую вы описали. Или, по крайней мере, я так думаю. И мне удалось решить эту проблему, следуя документации по предоставленным вами ссылкам.
В серверной документации указано, что формат авторизатора должен быть
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"
Крайне важно было добавить имя свойства. Это не будет работать без него, по крайней мере, на данный момент.
Подробнее см.
К сожалению, я не могу сказать, имеет ли этот подход некоторые ограничения по сравнению с вашим предложением определить авторизатор как ресурс. Фактически, это может упростить повторное использование одного и того же авторизатора в нескольких функциях одного и того же сервиса.
Serverless 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
Переход на общий пользовательский API Gateway Lambda Authorizer был несложным, когда он работал как часть службы. В этот момент нужно было просто добавить arn: к развернутой лямбда-выражению (авторизатору) и удалить определение "авторизатор" из службы в отдельную развертываемую службу.
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'
Тогда другая "служба" просто имеет несколько настраиваемых авторизаторов, совместно используемых несколькими развернутыми микросервисами.
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'
Вот как я настроил, так как ответ, опубликованный выше, у меня не сработал. Может быть, кому-то это пригодится.
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
, поскольку он уже указан в принятом ответе
И в моем случае я передал значение в заголовке события как тип авторизации, чтобы получить его в лямбда-функции авторизатора, и мой тип - ЗАПРОС