无服务器框架中的共享Lambda授权者设置

时间:2018-11-14 14:15:30

标签: amazon-web-services serverless-framework

我正在尝试创建一个自定义Lambda授权者,该授权者将在一些不同的服务/无服务器堆栈之间共享。如果我在这里https://serverless.com/framework/docs/providers/aws/events/apigateway/#note-while-using-authorizers-with-shared-api-gateway了解文档,则意味着我需要在“公共资源”服务/无服务器堆栈中创建共享授权者资源,然后从我的其他服务中引用该共享授权者。首先:我的理解正确吗?

如果我的理解正确,那么我的下一个问题将变成:我该怎么做?该文档没有为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正常工作时,是否只为我的Authorizer资源添加一个Output,然后从包含我的API Lambda的服务中为Fn :: ImportValue添加它?

在无服务器论坛上链接到我的问题以供后代使用:https://forum.serverless.com/t/shared-lambda-authorizer/6447

5 个答案:

答案 0 :(得分:3)

我最终使它能够工作,所以这是我设置身份验证器的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}

注意事项:尽管授权者函数被称为“授权者”,但与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 :(得分:2)

我遇到了与您描述的问题相同的问题。或者至少我是这样认为的。我设法通过遵循您提供的链接上的文档来解决它。

无服务器文档指出授权者格式为

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"

添加name属性至关重要。至少在目前,没有它将无法正常工作。

有关详细信息,请参见

不幸的是,与您将授权者定义为资源的建议相比,我不能说这种方法是否有某些局限性。实际上,这可能会使在同一服务中的多个功能中重复使用同一授权者变得容易。

答案 2 :(得分:0)

无服务器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

答案 3 :(得分:0)

这是我的设置方式,因为上面发布的答案对我不起作用。可能对某人有帮助。

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

,因为已经在接受的答案中指定了

  

在我的情况下,我在事件标头中将值传递为授权   键入以在authorizer lambda函数中获取它,我的类型是   请求

答案 4 :(得分:0)

更改为共享的自定义API网关Lambda授权器非常简单,因为它已作为服务的一部分工作。那时,它只是向已部署的lambda(授权者)添加了一个arn:,并将服务中的“ authorizer”定义从服务中删除到单独的可部署服务中。

df1 <- structure(list(id = 1:17, sex = c("Female", "Female", "Female", 
"Female", "Female", "Female", "Female", "Male", "Male", "Male", 
"Male", "Male", "Male", "Male", "Male", "Male", "Male"), age = c(1L, 
2L, 3L, 4L, 5L, 6L, 7L, 1L, 2L, 3L, 4L, 5L, 6L, 7L, 8L, 9L, 10L
), length = c(45L, 54L, 56L, 60L, 60L, 61L, 63L, 55L, 54L, 58L, 
61L, 65L, 63L, 65L, 67L, 68L, 69L)), class = "data.frame", row.names = c(NA, 
-17L))

lookup <- structure(list(sex = c("Female", "Female", "Female", "Female", 
"Female", "Female", "Female", "Male", "Male", "Male", "Male", 
"Male", "Male", "Male", "Male", "Male", "Male"), age = c(1L, 
2L, 3L, 4L, 5L, 6L, 7L, 1L, 2L, 3L, 4L, 5L, 6L, 7L, 8L, 9L, 10L
), length = c(50L, 53L, 56L, 58L, 60L, 61L, 63L, 50L, 54L, 57L, 
60L, 62L, 63L, 65L, 66L, 67L, 69L)), class = "data.frame", row.names = c(NA, 
-17L))

然后另一个“服务”只有一些自定义授权者,由多个部署的微服务共享。

  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'

旁注:

如果您想要一个令牌类型授权者,它将一个简单的事件转发“ authorization:bearer xyzsddfsf”:

functions:

  myCustomAuthorizer:
    name: something-else-myCustomAuthorizer
    handler: handler.myCustomAuthorizer
{
    "type": "TOKEN",
    "methodArn": "arn:aws:execute-api:region:####:apigwIdHere/dev/GET/path/to/resource",
    "authorizationToken": "Bearer ...."
}