XMLHttpRequest 无法加载 XXX 否 'Access-Control-Allow-Origin' header

XMLHttpRequest cannot load XXX No 'Access-Control-Allow-Origin' header

tl;博士;关于同源政策

我有一个启动 express.js 服务器实例的 Grunt 进程。在 Chrome(最新版本)的开发人员控制台的错误日志中出现以下错误日志时,直到现在它开始提供空白页面之前,它一直工作得很好:

XMLHttpRequest cannot load https://www.example.com/ No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:4300' is therefore not allowed access.

是什么阻止我访问该页面?

tl;dr — 末尾有摘要,答案中有标题,以便更容易找到相关部分。建议阅读所有内容,因为它为理解 why 提供了有用的背景知识,可以更轻松地了解 how 在不同情况下的应用方式。

关于同源政策

这是Same Origin Policy。这是浏览器实现的安全功能。

您的具体案例展示了它是如何为 XMLHttpRequest 实现的(如果您使用 fetch,您将获得相同的结果),但它也适用于其他事物(例如加载到 <canvas> 或加载到 <iframe> 中的文档),只是实现方式略有不同。

(奇怪的是,它也适用于 CSS 字体,但那是因为找到的铸造厂坚持 DRM,而不是为了同源策略通常涵盖的安全问题)。

证明需要 SOP 的标准场景可以用 three characters 来证明:

  • Alice 是一个有网络浏览器的人
  • Bob 经营一个网站(在您的示例中为 https://www.[website].com/
  • Mallory 经营一个网站(在您的示例中为 http://localhost:4300

Alice 登录到 Bob 的网站,那里有一些机密数据。可能是公司内网(只能通过局域网上的浏览器访问),或者是她的网上银行(只有输入用户名和密码后得到的 cookie 才能访问)。

Alice 访问 Mallory 的网站,该网站有一些 JavaScript 导致 Alice 的浏览器向 Bob 的网站发出 HTTP 请求(从她的 IP 地址和她的 cookie 等)。这可以像使用 XMLHttpRequest 和阅读 responseText.

一样简单

浏览器的同源策略阻止 JavaScript 读取 Bob 网站返回的数据(Bob 和 Alice 不希望 Mallory 访问)。 (请注意,例如,您可以跨源使用 <img> 元素显示图像,因为图像的内容不会暴露给 JavaScript(或 Mallory)……除非您抛出 canvas在这种情况下,您 生成 same-origin 违规错误)。


为什么在您认为不应该应用同源策略时应用

对于任何给定的 URL,可能不需要 SOP。出现这种情况的几个常见场景是:

  • Alice、Bob 和 Mallory 是同一个人。
  • Bob 提供了完整的 public 信息

... 但是浏览器无法知道以上任何一个是否为真,因此信任不是自动的,而是应用 SOP。在浏览器将提供给不同网站的数据之前,必须明确授予权限。


为什么同源策略只适用于网页中的JavaScript

浏览器扩展*、浏览器开发者工具中的网络选项卡和 Postman 等应用程序都是已安装的软件。他们不会将数据从一个网站传递到属于另一个网站 的 JavaScript 只是因为您访问了那个不同的网站。安装软件通常需要更有意识的选择。

没有第三方 (Mallory) 被视为风险。

* 浏览器扩展确实需要仔细编写以避免 cross-origin 问题。 See the Chrome documentation for example.


为什么不用JS读取就可以在页面显示数据

在许多情况下,Mallory 的网站可能会导致浏览器从第三方获取数据并显示它(例如,通过添加 <img> 元素来显示图像)。 Mallory 的 JavaScript 不可能读取该资源中的数据,只有 Alice 的浏览器和 Bob 的服务器可以这样做,所以它仍然是安全的。


CORS

错误消息中提到的 Access-Control-Allow-Origin HTTP response header 是 CORS 标准的一部分,它允许 Bob 明确授予允许 Mallory 的站点通过 Alice 的浏览器访问数据。

基本实现只包括:

Access-Control-Allow-Origin: *

…在响应中 headers 允许任何网站读取数据。

Access-Control-Allow-Origin: http://example.com/

… 将只允许特定站点访问它,Bob 可以根据 Origin request header 动态生成允许多个,但不是所有网站都可以访问它。

Bob 如何设置该响应的细节 header 取决于 Bob 的 HTTP 服务器 and/or server-side 编程语言。 Node.js/Express.js 的用户应该使用可能有帮助的 well-documented CORS middleware. Users of other platforms should take a look at this collection of guides for various common configurations

注意:有些请求很复杂,发送 preflight OPTIONS 请求,服务器必须在浏览器发送 JS 想要发出的 GET/POST/PUT/Whatever 请求之前响应该请求。仅将 Access-Control-Allow-Origin 添加到特定 URL 的 CORS 实现通常会因此而出错。


显然授予 p只有在以下任一情况下,Bob 才会通过 CORS 执行任务:

  • 数据不是私有的
  • 马洛里受到信任

如何添加这些 header?

这取决于您的 server-side 环境。

如果可以,请使用专为处理 CORS 而设计的库,因为它们会为您提供简单的选项,而不必手动处理所有内容。

Enable-Cors.org 列出了您可能会觉得有用的特定平台和框架的文档。

但我不是鲍勃!

Mallory 没有标准机制来添加这个 header 因为它必须来自 Bob 的网站,她无法控制。

如果 Bob 是 运行 public API 那么可能会有一种机制来打开 CORS(可能通过以某种方式格式化请求,或者之后的配置选项登录 Bob 站点的 Developer Portal 站点)。不过,这必须是 Bob 实施的机制。 Mallory 可以阅读 Bob 站点上的文档以查看是否有可用的内容,或者她可以与 Bob 交谈并要求他实施 CORS。


提到“预检响应”的错误消息

一些跨源请求是 preflighted

当(粗略地说)您尝试提出 cross-origin 请求时会发生这种情况:

  • 包括 cookie 等凭据
  • 无法使用常规 HTML 表单生成(例如,具有您无法在表单的 enctype 中使用的自定义 header 或 Content-Type ).

如果你正确地做了一些需要预检的事情

在这些情况下,此答案的其余部分仍然适用,但您还需要确保服务器可以侦听预检请求(这将是 OPTIONS(而不是 GETPOST 或您尝试发送的任何内容)并使用正确的 Access-Control-Allow-Origin header 以及 Access-Control-Allow-MethodsAccess-Control-Allow-Headers 以允许您的特定 HTTP 方法或 headers.

如果您错误地触发了预检

有时人们在尝试构造 Ajax 请求时会犯错误,有时这些会触发预检的需要。如果 API 旨在允许 cross-origin 请求,但不需要任何需要预检的东西,那么这可能会中断访问。

触发此问题的常见错误包括:

  • 正在尝试将 Access-Control-Allow-Origin 和其他 CORS 响应 header 放入请求中。这些不属于请求,不做任何有用的事情(你可以授予自己权限的权限系统有什么意义?),并且必须只出现在响应中。
  • 试图在一个没有请求body的GET请求上放一个Content-Type: application/jsonheader来描述(通常是当作者混淆了Content-TypeAccept).

在任何一种情况下,删除额外的请求 header 通常足以避免预检的需要(这将解决与支持简单请求的 API 通信时的问题但不是预检请求)。


不透明响应(no-cors 模式)

有时您需要发出 HTTP 请求,但不需要读取响应。例如如果您要将日志消息发布到服务器进行记录。

如果您正在使用 the fetch API(而不是 XMLHttpRequest),那么您可以将其配置为不尝试使用 CORS。

请注意,这不会让您执行需要 CORS 执行的任何操作您将无法阅读回复。您将无法提出需要预检的请求。

它会让您发出一个简单的请求,看不到响应,并且不会在开发人员控制台中填充错误消息。

当您使用 fetch 发出请求并且没有获得使用 CORS 查看响应的权限时,出现的 Chrome 错误消息解释了如何操作:

Access to fetch at 'https://example.com/' from origin 'https://example.net' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.

因此:

fetch("http://example.com", { mode: "no-cors" });

CORS 的替代方案

JSONP

Bob 还可以使用像 JSONP 这样的黑客来提供数据,这就是人们在 CORS 出现之前 cross-origin Ajax 的做法。

它以 JavaScript 程序的形式呈现数据,然后将数据注入 Mallory 的页面。

要求Mallory相信Bob不会提供恶意代码。

注意共同的主题:提供数据的网站必须告诉浏览器,第三方网站可以访问它发送给浏览器的数据。

由于 JSONP 的工作原理是附加一个 <script> 元素,以调用页面中已有函数的 JavaScript 程序的形式加载数据,尝试使用JSONP 在 URL 上的技术 returns JSON 会失败——通常会出现 CORB 错误——因为 JSON 不是 JavaScript.

将两个资源移动到一个Origi

如果 JS 运行的 HTML 文档和被请求的 URL 是同源的(共享相同的方案、主机名和端口),那么它们的同源策略授予权限默认。不需要 CORS。

一个代理

Mallory 可以 使用 server-side 代码来获取数据(然后她可以像往常一样通过 HTTP 从她的服务器传递到 Alice 的浏览器)。

它将:

  • 添加 CORS headers
  • 将响应转换为 JSONP
  • 与 HTML 文件同源

server-side 代码可以由第三方编写和托管(例如 CORS Anywhere)。请注意这对隐私的影响:第三方可以监控谁在他们的服务器上代理了什么。

Bob 不需要为此授予任何权限。

这里没有安全隐患,因为那只是在 Mallory 和 Bob 之间。 Bob 没有办法认为 Mallory 就是 Alice 并向 Mallory 提供 Alice 和 Bob 之间应该保密的数据。

因此,Mallory只能使用此技术来读取public数据。

但是请注意,从其他人的网站上获取内容并在您自己的网站上展示可能会侵犯版权,并可能会招致法律诉讼。

编写 Web 应用以外的东西

如“为什么同源策略仅适用于网页中的JavaScript”部分所述,您可以通过不在网页中写入JavaScript来避免SOP。

这并不意味着您不能继续使用 JavaScript 和 HTML,但您可以使用其他机制分发它,例如 Node-WebKit 或 PhoneGap。

浏览器扩展

在应用同源策略之前,浏览器扩展可能会在响应中注入 CORS header。

这些对于开发很有用,但对于生产站点并不实用(要求您站点的每个用户安装禁用其浏览器安全功能的浏览器扩展是不合理的)。

它们也倾向于只处理简单的请求(在处理预检 OPTIONS 请求时失败)。

拥有合适的本地开发环境服务器 通常是更好的方法。


其他安全风险

请注意,SOP/CORS 不会减轻 XSS, CSRF, or SQL Injection 需要独立处理的攻击。


总结

  • 您无法在您的client-side代码中执行任何操作来启用 CORS 访问某人其他人的服务器。
  • 如果您控制服务器,请求将发送至:向其添加 CORS 权限。
  • 如果你和控制它的人很友好:让他们给它添加 CORS 权限。
  • 如果是 public 服务:
    • 阅读他们的 API 文档,了解他们对使用 client-side JavaScript 访问它的看法:
      • 他们可能会告诉您使用特定的 URLs
      • 他们可能会支持 JSONP
      • 他们可能根本不支持从 client-side 代码进行 cross-origin 访问(这可能是基于安全原因的故意决定,特别是如果你必须通过一个个性化的 API 键每个请求)。
    • 确保您没有触发不需要的预检请求。 API 可能会授予简单请求的权限,但不会授予预检请求的权限。
  • 如果上述 none 适用:让浏览器改为与 您的 服务器通信,然后让您的服务器从其他服务器获取数据并通过它在。 (还有 third-party 托管服务将 CORS header 附加到 public 您可以使用的盟友可访问资源。

发生这种情况是因为 CORS 错误。 CORS 代表跨源资源共享。简而言之,当我们尝试从另一个域访问 domain/resource 时会发生此错误。

在此处阅读更多相关信息:CORS error with jquery

要解决此问题,如果您可以访问其他域,则必须在服务器中允许 Access-Control-Allow-Origin。这可以在headers中添加。您可以为所有 requests/domains 或特定域启用此功能。

How to get a cross-origin resource sharing (CORS) post request working

这些链接可能会有帮助

目标服务器必须允许跨源请求。为了允许它通过 express,只需处理 http options 请求:

app.options('/url...', function(req, res, next){
   res.header('Access-Control-Allow-Origin', "*");
   res.header('Access-Control-Allow-Methods', 'POST');
   res.header("Access-Control-Allow-Headers", "accept, content-type");
   res.header("Access-Control-Max-Age", "1728000");
   return res.sendStatus(200);
});

您应该启用 CORS 才能使其正常工作。

因为接受的答案中没有提到这一点。

  • 这个问题并非如此,但可能会帮助其他搜索该问题的人
  • 这是您可以在 client-code 中执行的操作,以防止 某些情况下 .
  • 出现 CORS 错误

你可以利用Simple Requests
为了执行 'Simple Requests' 请求需要满足几个条件。例如。只允许 POSTGETHEAD 方法,以及只允许一些给定的 Headers(你可以找到所有条件 here)。

如果您的客户端代码未在请求中使用固定值明确设置受影响的 Headers(例如 "Accept"),则某些客户端 可能 发生请使用一些 "non-standard" 值自动设置这些 Headers,导致服务器不接受它作为简单请求 - 这会给你一个 CORS 错误。

这个 CORS 问题没有进一步详细说明(出于其他原因)。

我目前遇到这个问题是出于不同的原因。 我的前端也返回 'Access-Control-Allow-Origin' header 错误。

只是我指出了错误的 URL 所以这个 header 没有正确反映(我一直认为它确实反映了)。本地主机(前端)-> 调用非安全 http(应该是 https),确保来自前端的 API 端点指向正确的协议。

"Get" 请求将 headers 转换附加到 "Options" 请求。所以出现Cors政策问题。您必须向您的服务器执行 "Options" 请求。

我在 Chrome 控制台中遇到了同样的错误。

我的问题是,我试图使用 http:// 而不是 https:// 访问该站点。所以没有什么可以解决的,只需要使用 https.

去同一个站点

这个错误让我花了 2 天的时间。我检查了我的服务器日志,浏览器 Chrome/Edge 和服务器之间的 Preflight Option request/response 没问题。主要原因是XHTMLRequest的GET/POST/PUT/DELETE server response还必须有下面的header:

access-control-allow-origin: origin  

“来源”在请求header中(浏览器会为您将其添加到请求中)。例如:

Origin: http://localhost:4221

您可以像下面这样添加响应 header 以接受所有人:

access-control-allow-origin: *  

或针对特定请求的响应 header,例如:

access-control-allow-origin: http://localhost:4221 

浏览器中的消息看不懂:"...请求的资源"

注意: CORS 适用于本地主机。不同的端口意味着不同的域。 如果收到错误消息,请检查服务器端的 CORS 配置。

在大多数住房服务中,只需在目标服务器文件夹的 .htaccess 中添加:

Header 设置 Access-Control-Allow-Origin 'https://your.site.folder'

我遇到了同样的问题。在我的例子中,我通过将 timestamp 的附加参数添加到我的 URL 来修复它。即使这不是我访问的服务器所要求的。

例如您的网址。com/yourdocument?timestamp=1234567

注:我用的是epos时间戳