无服务器框架中的共享 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'