由于 AWS 中的 CORS 阻止来源而导致访问被拒绝 Api-Gateway

Getting access denied due to origin blocked by CORS in AWS Api-Gateway

我有一个 AWS SAM 模板,它在 API 网关中创建 lambda 函数和 post 方法。默认情况下,它使用 Lambda 代理集成,当我通过 PostMan 工具进行测试时它工作正常,但是当我将 API 网关 URL 与我的沙箱应用程序一起使用时,它显示以下错误。

Access to XMLHttpRequest at 'https://abcdef.execute-api.eu-west-2.amazonaws.com/dev/my-api' from origin 'https://abcd.csb.app' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: It does not have HTTP ok status.

但是当我手动创建 API 网关 post 方法并尝试时它工作正常。

Lambda 函数也在响应中返回以下 header。

response = {
        'statusCode': status_code,
        'headers': {
            'Access-Control-Allow-Headers': 'Content-Type',
            'Access-Control-Allow-Origin': '*',
            'Access-Control-Allow-Methods': 'OPTIONS,POST'
        },
        'body': json.dumps(response_data)
    }

以下是 AWS SAM 模板。

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: >
  AWS SAM Template

# More info about Globals: https://github.com/awslabs/serverless-application-model/blob/master/docs/globals.rst
Globals:
  Function:
    Timeout: 10

Parameters:
  DeploymentEnv:
    Type: String

Resources:
  ApiGatewayApi:
    DependsOn: LambdaFunction
    Type: AWS::Serverless::Api
    Properties:
      StageName: !Ref DeploymentEnv
      EndpointConfiguration: 
        Type: REGIONAL
      Cors:
        AllowMethods: "'POST,OPTIONS'"
        AllowHeaders: "'Content-Type,X-Amz-Date,Authorization,X-Api-Key'"
        AllowOrigin: "'*'"
        MaxAge: "'600'"
        AllowCredentials: false
      Auth:
        DefaultAuthorizer: NONE
        ApiKeyRequired: true # sets for all methods
  
  LambdaFunction:
    Type: AWS::Serverless::Function # More info about Function Resource: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#awsserverlessfunction
    Properties:
      FunctionName: !Join [ "", [ !Ref DeploymentEnv, "-my-lambda"]]
      CodeUri: my_api/
      Handler: app.lambda_handler
      Runtime: python3.9
      Architectures:
        - x86_64
      Events:
        EventTriggerlambda:
          Type: Api # More info about API Event Source: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#api
          Properties: 
            RestApiId: !Ref ApiGatewayApi
            Path: /my-api
            Method: POST
            Auth:
              ApiKeyRequired: true
      Role: Role_URN
      Environment:
        Variables:
          URI: Test
          USER_NAME: Test
          PASSWORD: Test
  
  ApiKey:
    Type: AWS::ApiGateway::ApiKey
    DependsOn: ApiGatewayApiStage
    Properties:
      Name: !Join ["", [{"Ref": "AWS::StackName"}, "-apikey"]]
      Enabled: true
      StageKeys:
        - RestApiId: !Ref ApiGatewayApi
          StageName: !Ref DeploymentEnv
  
  UsagePlan:
    DependsOn: 
      - ApiGatewayApiStage
    Type: AWS::ApiGateway::UsagePlan
    Properties:
      ApiStages:
        - ApiId: !Ref ApiGatewayApi
          Stage: !Ref DeploymentEnv
      Throttle:
        BurstLimit: 500
        RateLimit: 100
      UsagePlanName: MY-UsagePlan
      
  UsagePlanKey:
    Type: AWS::ApiGateway::UsagePlanKey
    Properties:
      KeyId: !Ref ApiKey
      KeyType: API_KEY
      UsagePlanId: !Ref UsagePlan

Outputs:
  LambdaFunction:
    Description: "Lambda Function ARN"
    Value: !GetAtt LambdaFunction.Arn

请帮忙,谢谢:)

配置不适合在 AWS SAM 模板的 API-Gateway 中创建 API。因为 SAM 部署默认使用 lambda 代理集成,这就是为什么在方法响应中,需要的值很少,无法使用上述配置自动设置。因此,我使用开放 API 规范,其中我定义了 Rest API 配置,部署后无需任何手动干预即可正常工作。

如下配置即可。

ApiGatewayApi:
    DependsOn: LambdaFunction
    Type: AWS::Serverless::Api
    Properties:
      StageName: !Ref DeploymentEnv
      DefinitionBody:
          'Fn::Transform':
            Name: 'AWS::Include'
            Parameters:
              Location: !Join [ '', [ 's3://mybucket', '/openapi-spec.yaml'  ] ]
      EndpointConfiguration: 
        Type: REGIONAL

OpenAPI 配置

openapi: "3.0.1"
info:
  title: "test-api"
  description: "Created by AWS Lambda"
  version: "2022-01-07T18:00:40Z"

paths:
  /test-api:
    post:
      responses:
        "200":
          description: "200 response"
          headers:
            Access-Control-Allow-Origin:
              schema:
                type: "string"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Empty"
      x-amazon-apigateway-integration:
        httpMethod: "POST"
        uri:  
          Fn::Sub: "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${LambdaFunction.Arn}/invocations"
        responses:
          default:
            statusCode: "200"
            responseParameters:
              method.response.header.Access-Control-Allow-Origin: "'*'"
        passthroughBehavior: "when_no_match"
        contentHandling: "CONVERT_TO_TEXT"
        type: "aws_proxy"
    options:
      responses:
        "200":
          description: "200 response"
          headers:
            Access-Control-Allow-Origin:
              schema:
                type: "string"
            Access-Control-Allow-Methods:
              schema:
                type: "string"
            Access-Control-Allow-Headers:
              schema:
                type: "string"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Empty"
      x-amazon-apigateway-integration:
        responses:
          default:
            statusCode: "200"
            responseParameters:
              method.response.header.Access-Control-Allow-Methods: "'DELETE,GET,HEAD,OPTIONS,PATCH,POST,PUT'"
              method.response.header.Access-Control-Allow-Headers: "'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token'"
              method.response.header.Access-Control-Allow-Origin: "'*'"
        requestTemplates:
          application/json: "{\"statusCode\": 200}"
        passthroughBehavior: "when_no_match"
        type: "mock"
    x-amazon-apigateway-any-method:
      responses:
        "200":
          description: "200 response"
          content: {}
      security:
      - api_key: []
      x-amazon-apigateway-integration:
        httpMethod: "POST"
        uri: "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:${LambdaFunction.Arn}/invocations"
        responses:
          ".*":
            statusCode: "200"
        passthroughBehavior: "when_no_match"
        type: "aws_proxy"
components:
  schemas:
    Empty:
      title: "Empty Schema"
      type: "object"
  securitySchemes:
    api_key:
      type: "apiKey"
      name: "x-api-key"
      in: "header"

此处 openapi-spec.yaml 文件保存在与 AWS SAM 模板相同的文件夹中,并在部署开始之前使用 GitHub 工作流管道文件中的以下命令上传到 S3 存储桶.

- run: aws s3 cp openapi-spec.yaml s3://mnai-code-deployments
        - run: sam build
        - run: sam deploy --no-confirm-changeset --no-fail-on-empty-changeset --stack-name my-stack --s3-bucket mybucket  --capabilities CAPABILITY_IAM --region eu-west-2 --parameter-overrides ParameterKey=DeploymentEnv,ParameterValue=dev ParameterKey=S3Bucket,ParameterValue=mybucket

谢谢