带有 Terraform 和 OpenAPI 的 AWS API GW 未启用 CORS 设置

问题描述 投票:0回答:1

我正在使用 terraform 与 AWS 提供商来创建 API 网关。除了 CORS 设置之外,一切似乎都很好。我还使用 OpenAPI 进行配置。

我的“最终用户”问题是,我对后端的 API 调用返回 404 not found,因为它首先发出 OPTIONS 请求。在 API Gateway 中启用 CORS 设置可以为您处理 CORS,并自动返回 OPTIONS 请求的正确响应。但是,CORS 设置尚未启用。我相信它们应该是由于 x-amazon-apigateway-cors 部分造成的。

这是我的 OpenAPI 文件:

openapi: 3.0.1

info:
  title: App API
  description: App API
  version: 0.1.0

paths:
  '/requisitions':
    post:
      operationId: createRequisition
      summary: Create requisition
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - institutionId
              properties:
                institutionId:
                  type: string
      responses:
        '200':
          description: 200 response
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Requisition'
        '401':
          $ref: '#/components/responses/Unauthenticated'
        default:
          $ref: '#/components/responses/Error'
      x-amazon-apigateway-integration:
        type: AWS_PROXY
        httpMethod: POST
        uri: '${gocardless_function_arn}'
        payloadFormatVersion: 2.0

security:
  - cognito-jwt: []

x-amazon-apigateway-cors:
  allowOrigins:
    - '*'
  allowMethods:
    - GET
    - OPTIONS
    - POST
  allowHeaders:
    - x-amzm-header
    - x-apigateway-header
    - x-api-key
    - authorization
    - x-amz-date
    - content-type

components:
  schemas:
    AnyValue:
      nullable: true
      description: Can be any value - null, string, number, boolean, array or object.
    Requisition:
      type: object
      properties:
        id:
          type: string
          nullable: true
    Error:
      type: object
      required:
        - status
        - statusCode
        - error
      properties:
        status:
          type: string
        statusCode:
          type: integer
        requestId:
          type: string
        documentationUrl:
          type: string
        error:
          type: object
          required:
            - code
            - message
            - timestamp
          properties:
            code:
              type: string
            message:
              type: string
            details:
              $ref: '#/components/schemas/AnyValue'
            timestamp:
              type: string
            path:
              type: string
            suggestion:
              type: string

  securitySchemes:
    cognito-jwt:
      type: oauth2
      flows:
        authorizationCode:
          authorizationUrl: 'https://cognito-idp.eu-west-2.amazonaws.com/'
          tokenUrl: ''
          scopes: {}

      x-amazon-apigateway-authorizer:
        type: jwt
        jwtConfiguration:
          issuer: 'https://cognito-idp.eu-west-2.amazonaws.com/${cognito_user_pool_id}'
          audience:
            - '${cognito_app_client_id}'
        identitySource: '$request.header.Authorization'

  responses:
    Unauthenticated:
      description: Unauthenticated
      headers:
        www-authenticate:
          schema:
            type: string
      content:
        application/json:
          schema:
            type: object
            required:
              - message
            properties:
              message:
                type: string
    Error:
      description: Error
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'

这是我的地形的相关部分:

resource "aws_apigatewayv2_api" "api_gateway" {
  name          = "API Gateway"
  version       = "0.1.0"
  protocol_type = "HTTP"
  description   = "API gateway"
  body = templatefile("${path.module}/files/api-gateway-openapi.yaml", {
    gocardless_function_arn = module.gocardless_create_requisition_lambda.lambda_function_arn,
    cognito_user_pool_id    = aws_cognito_user_pool.pool.id,
    cognito_app_client_id   = aws_cognito_user_pool_client.client.id,
    region                  = var.region
  })
}

resource "aws_apigatewayv2_deployment" "api_gateway_deployment" {
  api_id      = aws_apigatewayv2_api.api_gateway.id
  description = "API Gateway deployment"

  triggers = {
    redeployment = sha1(join(
      ",", tolist([
        templatefile("${path.module}/files/api-gateway-openapi.yaml", {
          gocardless_function_arn = module.gocardless_create_requisition_lambda.lambda_function_arn,
          cognito_user_pool_id    = aws_cognito_user_pool.pool.id,
          cognito_app_client_id   = aws_cognito_user_pool_client.client.id,
          region                  = var.region
        }),
        jsonencode(aws_apigatewayv2_stage.api_gateway_default_stage)
      ])
    ))
  }
}

resource "aws_apigatewayv2_stage" "api_gateway_default_stage" {
  api_id      = aws_apigatewayv2_api.api_gateway.id
  name        = "default"
  auto_deploy = true
  default_route_settings {
    throttling_rate_limit  = 10
    throttling_burst_limit = 10
  }
}

我尝试通过 AWS 控制台中的“ClickOps”手动启用 CORS。来自我前端的请求立即开始工作(选项,然后是 POST)。

我期待 x-amazon-apigateway-cors (https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-swagger-extensions-cors-configuration.html) 部分已经启用了这些 CORS 选项,而无需我手动执行它们。通过手动执行它们,每次我重新运行时它们也会被覆盖

terraform apply

我应该在 OpenAPI 文件中更改哪些内容才能在 API GW 中启用 CORS 配置?

amazon-web-services terraform aws-api-gateway openapi
1个回答
0
投票

您需要为选项添加模拟集成的想法。我已经编写了一个将 CORS 应用于 API 网关的模块,因此 main.tf 将是。

  ####CORS####
resource "aws_api_gateway_resource" "cors" {
  rest_api_id = var.api_id
  parent_id   = var.api_resource_id
  path_part   = "{cors+}"
}

resource "aws_api_gateway_method" "cors" {
  rest_api_id   = var.api_id
  resource_id   = aws_api_gateway_resource.cors.id
  http_method   = "OPTIONS"
  authorization = "NONE"
}

resource "aws_api_gateway_integration" "cors" {
  rest_api_id = var.api_id
  resource_id = aws_api_gateway_resource.cors.id
  http_method = aws_api_gateway_method.cors.http_method
  type = "MOCK"
      request_templates = {
    "application/json" = jsonencode(
      {
        statusCode = 200
      }
    )
  }
}

resource "aws_api_gateway_method_response" "cors" {
  depends_on = [aws_api_gateway_method.cors]
  rest_api_id = var.api_id
  resource_id = aws_api_gateway_resource.cors.id
  http_method = aws_api_gateway_method.cors.http_method
  status_code = 200
  response_parameters = {
    "method.response.header.Access-Control-Allow-Origin" = true,
    "method.response.header.Access-Control-Allow-Methods" = true,
    "method.response.header.Access-Control-Allow-Headers" = true
  }
  response_models = {
    "application/json" = "Empty"
  }
}

resource "aws_api_gateway_integration_response" "cors" {
  depends_on = [aws_api_gateway_integration.cors, aws_api_gateway_method_response.cors]
  rest_api_id = var.api_id
  resource_id = aws_api_gateway_resource.cors.id
  http_method = aws_api_gateway_method.cors.http_method
  status_code = 200
  response_parameters = {
    "method.response.header.Access-Control-Allow-Origin" = "'*'", # replace with hostname of frontend (CloudFront)
    "method.response.header.Access-Control-Allow-Headers" = "'Content-Type'",
    "method.response.header.Access-Control-Allow-Methods" = "'GET, POST, DELETE'" # remove or add HTTP methods as needed
  }
}

############

Variables.tf 文件将是

# -----------------------------------------------------------------------------
# Variables: API Gateway
# -----------------------------------------------------------------------------

# var.api_id
variable "api_id" {
  description = "API identifier"
}

# var.api_resource_id
variable "api_resource_id" {
  description = "API resource identifier"
}

# -----------------------------------------------------------------------------
# Variables: CORS-related
# -----------------------------------------------------------------------------

# var.allow_headers
variable "allow_headers" {
  description = "Allow headers"
  type        = list(string)

  default = [
    "Authorization",
    "Content-Type",
    "X-Amz-Date",
    "X-Amz-Security-Token",
    "X-Api-Key",
  ]
}

# var.allow_methods
variable "allow_methods" {
  description = "Allow methods"
  type        = list(string)

  default = [
    "OPTIONS",
    "HEAD",
    "GET",
    "POST",
    "PUT",
    "PATCH",
    "DELETE",
  ]
}

# var.allow_origin
variable "allow_origin" {
  description = "Allow origin"
  type        = string
  default     = "*"
}

# var.allow_max_age
variable "allow_max_age" {
  description = "Allow response caching time"
  type        = string
  default     = "7200"
}

# var.allowed_credentials
variable "allow_credentials" {
  description = "Allow credentials"
  default     = false
}

输出.tf

output "cors_resource_id" {
  value       =  aws_api_gateway_resource.cors.id
} 

然后就可以使用这个模块了

resource "aws_api_gateway_rest_api" "api" {
  name = var.api_name
  tags = var.tags
}

####CORS####

module "cors" {
  source = "../api-gateway-cors"

  api_id          = aws_api_gateway_rest_api.api.id
  api_resource_id = aws_api_gateway_rest_api.api.root_resource_id
}

############
© www.soinside.com 2019 - 2024. All rights reserved.