如何在无服务器中允许 CORS 用于自定义标头?

Posted

技术标签:

【中文标题】如何在无服务器中允许 CORS 用于自定义标头?【英文标题】:How to allow CORS for custom headers in Serverless? 【发布时间】:2018-06-08 17:06:42 【问题描述】:

这里的核心问题是:“如何在使用无服务器框架处理的 CORS GET 请求中允许自定义标头?”。如果您知道答案,请通过 Go,收集 200 美元并请回答该问题。如果这不是一个直接回答的问题,这里是详细信息:

我正在使用 AWS Lambda 上的无服务器框架编写一个应用程序(API 是通过 AWS API Gateway 管理的。坦率地说,我不完全确定这意味着什么或给我带来了什么好处,但这就是无服务器自动为我配置的)。我正在尝试创建一个需要启用 CORS 的开放 API。我正在使用 Lambda 代理集成。我遵循了here 的做法。他们给我带来了部分成功。如果我不包含自定义标头,我的应用程序当前启用了 CORS。但是,它仍然不适用于自定义标题。

当我向我的 API 发送以下请求时:

var data = null;

var xhr = new XMLHttpRequest();
xhr.withCredentials = false;

xhr.addEventListener("readystatechange", function () 
  if (this.readyState === 4) 
    console.log(this.responseText);
  
);

xhr.open("GET", "https://api.spongebobify.com/");
xhr.setRequestHeader("text", "hey");


xhr.send(data);

...我收到此错误:

Failed to load https://api.spongebobify.com/: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'https://forum.serverless.com' is therefore not allowed access.

如果我使用 Chrome 开发工具检查“响应标头”,则确认此错误消息:响应标头中没有 Access-Control-Allow-Origin。

但是,如果我发送相同的请求并注释掉 setRequestHeader(),它会完美运行(是的,我知道它返回 403 错误:这是故意行为)。

这就是我认为正在发生的事情。我的服务有两个潜在的 CORS 问题:域相关(不是来自原始域的请求)和自定义标头相关(CORS 规范未安全列出的标头,更多 here)。不知何故,Serverless 框架遇到了第二个问题,这导致它甚至无法发布适当的标头以允许所有 ("*") 域。

这是我的 serverless.yml 配置文件:

# serverless.yml

service: spongebobify

provider:
  name: aws
  runtime: nodejs6.10
  stage: dev
  region: us-east-1

functions:
  app:
    handler: handler.endpoint
    events:
      - http: GET /
        cors:
            origin: '*'
            headers:
              - Content-Type
              - X-Amz-Date
              - Authorization
              - X-Api-Key
              - X-Amz-Security-Token
              - X-Amz-User-Agent
              - Startlower
              - Text
              - Access-Control-Allow-Headers
              - Access-Control-Allow-Origin
            allowCredentials: false

这是我要运行的函数。您可以看到我 许多 尝试正确设置标题。我 60% 确信此时将通过 serverless.yml 文件进行修复。

"use strict";

const spongebobify = require("spongebobify");

module.exports.endpoint = (event, context, callback) => 
  let startLower = event.headers.startlower === "false" ? false : true;
  try 
    const response = 
      statusCode: 200,
      headers: 
        "Access-Control-Allow-Origin": "*", // Required for CORS support to work
        "Access-Control-Allow-Headers": "content-type,origin,text,startlower",
        "Access-Control-Allow-Methods": "GET, OPTIONS",
        "content-type": "text/plain",
        "Access-Control-Allow-Credentials": true // Required for cookies, authorization headers with HTTPS
      ,
      body: spongebobify(event.headers.text, startLower)
    ;
    callback(null, response);
   catch (err) 
    console.log(err);
    const response = 
      statusCode: 403,
      headers: 
        "Access-Control-Allow-Origin": "*", // Required for CORS support to work
        "Access-Control-Allow-Headers": "content-type,origin,X-text,startlower",
        "Access-Control-Allow-Methods": "GET, OPTIONS",
        "content-type": "text/plain",
        "Access-Control-Allow-Credentials": true // Required for cookies, authorization headers with HTTPS
      ,
      body: "Malformed request."
    ;
    callback(null, response);
  
;

您可以复制我在以下站点的开发控制台中运行上述XMLHttpRequest 的问题:

    api.spongebobify.com 启用或禁用自定义标头。它在这两种情况下都能完美运行(因为它不会是跨源的)。 任何没有正确配置 CSP 并启用自定义标头的站点。 OPTIONS 请求会失败,它会准确报告没有 Access-Control-Allow-Origin 标头 没有正确配置 CSP没有启用自定义标头的任何站点。 OPTIONS 请求将通过(您会知道,因为 Chrome 永远不会告诉您它发生了),您将在响应标头中看到 Access-Control-Allow-Origin。您还将看到响应“格式错误的请求”。

【问题讨论】:

我对无服务器一无所知,但通过查看问题中的 serverless.yml 文件,我看到了用于处理 GET 请求的配置,但没有任何用于处理 OPTIONS 请求的配置。要获取 cors 配置,您不需要显式处理对该文件的 OPTIONS 请求吗? 一开始我也是这么想的——Serverless 文档中没有任何关于它的内容,而且它似乎可以在没有自定义标头的情况下很好地处理 OPTIONS,所以我怀疑就是这样。 问题是,如果没有将自定义标头添加到请求中,您的浏览器就没有执行 OPTIONS 请求。 xhr.setRequestHeader("text", "hey") 代码是导致浏览器发送 OPTIONS 请求的原因。当您删除它时,浏览器只是直接发送 GET 请求,而无需先执行任何 OPTIONS 预检。 它不是必须首先发送一个 OPTIONS 请求来验证域是否对 CORS 请求开放吗? 否 - 请参阅 developer.mozilla.org/en-US/docs/Web/HTTP/CORS#Simple_requests。您的无添加标头 GET 请求是一个不需要预检的“简单请求” 【参考方案1】:

问题是Prefilght request 是如何工作的,根据我的经验,这个过程不能很好地仅在 GET 方法上配置自定义标头,因此,只需添加 POST 方法并修复 CORS 问题:

events:
  - http:
      path: admin/type
      method: get
      cors:
        origin: '*'
        headers:
          - token
        allowCredentials: false
  - http:
      path: admin/type
      method: post
      cors:
        origin: '*'
        headers:
          - token
        allowCredentials: false

这个CORS and API Gateway survival guide 和Fixing Common Problems with CORS and javascript 帮助我理解问题和解决方案。

这是我的 JS 代码

fetch(api_url,
  method : 'GET',
  mode: 'cors',
  headers: 
    token: 'XXXX-XXXX'
  
).then((resp) => resp.json())

【讨论】:

【参考方案2】:

我认为问题在于您将 HTTP 事件的短格式 (- http: GET /) 与添加其他选项的长格式混合在一起。

试试这个:

functions:
  app:
    handler: handler.endpoint
    events:
      - http: 
          method: GET 
          path: /
          cors:
            origin: '*'
            headers:
              - Content-Type
              - X-Amz-Date
              - Authorization
              - X-Api-Key
              - X-Amz-Security-Token
              - X-Amz-User-Agent
              - Startlower
              - Text
              - Access-Control-Allow-Headers
              - Access-Control-Allow-Origin
            allowCredentials: false

主要变化有:

1) 在http 事件对象上添加methodpath 键,以及

2) 将cors 对象缩进另一个级别。它之前位于http 事件的顶层。

如果这有帮助,请告诉我:)

【讨论】:

这确实有效 - 尽管我只是从标题切换到查询参数,因为它让我的生活变得更轻松。不过谢谢! 是否可以为事件中的每个请求定义这些标头或所有请求? @亚历克斯

以上是关于如何在无服务器中允许 CORS 用于自定义标头?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Google Cloud Endpoints 中允许 CORS?

路径不适用于 Blazor 中允许的 IdentityServer CORS 端点,但适用于 Postman

如何在 node.js 中允许 CORS?

如何在 NodeJS 14 中允许 CORS

如何在我的谷歌脚本中允许 CORS 请求?

如何在旧版本的放心中允许自签名 SSL 证书