如何使用 OpenAPI 为 AWS API Gateway 配置 CORS?

Posted

技术标签:

【中文标题】如何使用 OpenAPI 为 AWS API Gateway 配置 CORS?【英文标题】:How to configure CORS for AWS API Gateway using OpenAPI? 【发布时间】:2020-10-25 01:54:51 【问题描述】:

我有一个通过 CDK 部署的 API 的 OpenAPI 规范。规范看起来像:

openapi: 3.0.1
info:
  title: My API
  description: My REST API with CORS enabled
  version: 0.1.0

x-amazon-apigateway-cors:
  allowOrigins:
    - "*"
  allowCredentials: true
  exposeHeaders:
    - "x-apigateway-header"
    - "x-amz-date"
    - "content-type"
  maxAge: 3600
  allowMethods:
    - "*"
  allowHeaders":
    - "x-apigateway-header"
    - "x-amz-date"
    - "content-type"
    - "Authorization"

components:
  securitySchemes:
    lambda:
      type: "apiKey"
      name: "Authorization"
      in: "header"
      x-amazon-apigateway-authtype: "custom"
      x-amazon-apigateway-authorizer:
        authorizerUri: "my-lambda-authorizer"
        authorizerResultTtlInSeconds: 300
        type: "token" 

paths:
  /user/id:
    get:
      summary: Get info of specified user.
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/User'
      security:
        - lambda: []
      x-amazon-apigateway-integration:
        uri: "my-lambda"
        passthroughBehavior: "when_no_match"
        httpMethod: "POST"
        type: "aws_proxy"

当我尝试通过fetch() 访问它时,我收到错误Failed to load resource: Origin http://localhost:8000 is not allowed by Access-Control-Allow-Origin

fetch('https://api.example.com/user/1')
        .then(response => response.json())
        .then((user: User) => 
            // do something
        )
        .catch((err) => 
            console.log("Error: " + err);
        );

API 可通过api.example.com 访问,我正在使用 Gatsby 在本地运行网站localhost:8000

AWS docs 似乎表明当我将 x-amazon-apigateway-cors 放在规范的根目录时启用了 CORS,但是当我尝试访问 API 时似乎没有启用(或工作)CORS。如何在无需在控制台中配置的情况下为我的 API 启用 CORS?

【问题讨论】:

【参考方案1】:

Amazon API Gateway 提供两种类型的 API:REST API 和 HTTP API。 REST API 是最初随 Amazon API Gateway 引入的那种 API,而 HTTP APIs got announced at the end of 2019。

根据您的 OpenAPI 规范的描述,我假设您正在尝试部署 REST API。 x-amazon-apigateway-cors OpenAPI 扩展仅适用于 HTTP API。

您现在有两个选择:要么切换到使用 HTTP API,要么手动配置 CORS。只要您不需要features only supported by REST APIs,我建议您改用 HTTP API,因为这是 Amazon API Gateway 提供的更现代的 API。

如果您想使用 REST API,启用 CORS 需要更多手动配置。 AWS 在Enabling CORS for a REST API resource 中记录了这一点。本质上,您必须确保您的集成返回正确的 CORS 标头。对于 NodeJS AWS Lambda 函数,可能如下所示:

exports.handler = async (event) => 
    const response = 
        statusCode: 200,
        headers: 
            "Access-Control-Allow-Headers" : "Content-Type",
            "Access-Control-Allow-Origin": "https://www.example.com",
            "Access-Control-Allow-Methods": "OPTIONS,POST,GET"
        ,
        body: JSON.stringify('Hello from Lambda!'),
    ;
    return response;
;

要使 CORS 飞行前请求正常工作,您还必须确保 OPTIONS 请求也返回正确的标头。实现这一目标的最简单方法是将 OPTIONS 请求的模拟集成添加到您的 OpenAPI 规范中。看起来像:

options:
  responses:
    '200':
      description: Default response
      headers:
        Access-Control-Allow-Headers:
          schema:
            type: string
        Access-Control-Allow-Methods:
          schema:
            type: string
        Access-Control-Allow-Origin:
          schema:
            type: string
  x-amazon-apigateway-integration:
    type: mock
    requestTemplates:
      application/json: |
        "statusCode" : 200
    responses:
      default:
        statusCode: 200
        responseParameters:
          method.response.header.Access-Control-Allow-Headers: "'*'"
          method.response.header.Access-Control-Allow-Methods: "'OPTIONS,POST'"
          method.response.header.Access-Control-Allow-Origin: "'https://example.com/'"

还请注意modern browsers don't support localhost as origin for CORS,因此您可能还需要解决这个问题。

【讨论】:

谢谢,这是一个非常有帮助的答案。你是对的,它是一个 REST API,因为 (a) 它是唯一通过 CDK 支持 OpenAPI 的 API,并且 (b) 我需要通过检查令牌来进行授权,因为它看起来不像 Cognito 授权方足够丰富我想要。根据您的回答,我已经设法通过从代理本身返回标头来使 CORS 工作,但前提是我禁用了自定义 lambda 授权程序。为了能够在这里使用 lambda 授权器,我还需要做些什么吗? 您的客户端请求是否成功通过了授权人?因为如果不是,授权方将生成响应,其中不包含必要的 CORS 标头。 有没有办法让授权方返回标头?文档建议抛出一个异常来指示 401,所以我猜这会导致标题不被传递。 原来在 *** 上有另一个问题:***.com/questions/36913196/… :-) 非常感谢

以上是关于如何使用 OpenAPI 为 AWS API Gateway 配置 CORS?的主要内容,如果未能解决你的问题,请参考以下文章

terraform api 网关与 openapi 规范的集成

针对匹配模式的 aws api 网关标头验证

AWS SAM :: AWS::Serverless::Api“'Auth' 属性的值无效”

如何在 OpenAPI (Swagger) 中为相同的 HTTP 状态代码定义不同的响应?

如何使用 Springdoc 从 OpenAPI 文档中隐藏端点

深入理解云计算OpenAPI体系