Access-Control-Allow-Origin 标头如何工作?

显然,我完全误解了它的语义。我想到了这样的事情:

  1. 客户端从 http:// siteA- origin下载 javascript 代码 MyCode.js。
  2. MyCode.js 的响应标头包含Access-Control-Allow-Origin:http:// siteB ,我认为这意味着 MyCode.js 被允许对站点 B 进行跨域引用。
  3. 客户端触发了 MyCode.js 的某些功能,该功能继而向 http:// siteB 发出了请求,尽管这是跨域请求,但仍然可以。

好吧,我错了。它根本不像这样工作。因此,我阅读了跨域资源共享,并尝试阅读w3c 建议中的跨域资源共享

可以确定的一件事 - 我仍然不明白我应该如何使用此标头。

我对站点 A 和站点 B 都拥有完全控制权。如何使用此标头使从站点 A 下载的 javascript 代码能够访问站点 B 上的资源?

聚苯乙烯

我不想利用 JSONP。

答案

Access-Control-Allow-OriginCORS(跨源资源共享)标头

当站点 A 尝试从站点 B 获取内容时,站点 B 可以发送Access-Control-Allow-Origin响应标头,以告知浏览器某些原始来源可以访问此页面的内容。 ( 来源域,再加上方案和端口号 。)默认情况下, 其他任何来源无法访问站点 B 的页面。使用Access-Control-Allow-Origin标头会打开一扇门,可以通过特定的请求来源进行跨域访问。

对于站点 B 希望站点 A 可以访问的每个资源 / 页面,站点 B 应在其页面上提供响应标头:

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

现代浏览器不会完全阻止跨域请求。如果站点 A 从站点 B 请求页面,则浏览器实际上将在网络级别获取请求的页面并检查响应头是否将站点 A 列为允许的请求者域。如果站点 B 未指示允许站点 A 访问此页面,则浏览器将触发XMLHttpRequesterror事件,并拒绝对请求 JavaScript 代码的响应数据。

非简单请求

什么发生在网络层面可以比上述解释稍微复杂一些 。如果该请求是“非简单” 请求 ,则浏览器将首先发送一个无数据的 “预检” OPTIONS 请求,以验证服务器将接受该请求。当一个(或两个)同时发生时,请求是不简单的:

  • 使用 GET 或 POST 以外的 HTTP 动词(例如 PUT,DELETE)
  • 使用非简单的请求标头;仅有的简单请求标头是:
    • Accept
    • Accept-Language
    • Content-Language
    • Content-Type (仅当其值为application/x-www-form-urlencodedmultipart/form-datatext/plain时,这才简单)

如果服务器使用适当的响应标头(非简单标头的Access-Control-Allow-Headers ,非简单动词的Access-Control-Allow-Methods )响应非简单动词和 / 或非匹配的 OPTIONS 预检响应 - simple 标头,然后浏览器发送实际请求。

假设站点 A 要发送对/somePage的 PUT 请求,其非简单的Content-Type值为application/json ,则浏览器将首先发送预检请求:

OPTIONS /somePage HTTP/1.1
Origin: http://siteA.com
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: Content-Type

请注意,浏览器会自动添加Access-Control-Request-MethodAccess-Control-Request-Headers 。您无需添加它们。该 OPTIONS 预检获取成功的响应头:

Access-Control-Allow-Origin: http://siteA.com
Access-Control-Allow-Methods: GET, POST, PUT
Access-Control-Allow-Headers: Content-Type

发送实际请求时(完成预检后),其行为与处理简单请求的方式相同。换句话说,将预检成功的非简单请求与简单请求视为相同(即,服务器必须仍然为实际响应再次发送Access-Control-Allow-Origin )。

浏览器发送实际请求:

PUT /somePage HTTP/1.1
Origin: http://siteA.com
Content-Type: application/json

{ "myRequestContent": "JSON is so great" }

然后服务器发送一个Access-Control-Allow-Origin ,就像处理一个简单的请求一样:

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

有关非简单请求的更多信息,请参阅通过 CORS 理解 XMLHttpRequest

跨域请求共享 - 根据 Same-Origin-Policy, CORS (AKA 跨域 AJAX 请求)是大多数 Web 开发人员可能遇到的问题,浏览器将客户端 JavaScript 限制在安全沙箱中,通常 JS 无法直接与远程通信来自其他域的服务器。过去,开发人员创建了许多棘手的方法来实现跨域资源请求,最常用的方法是:

  1. 使用 Flash / Silverlight 或服务器端作为 “代理” 与远程通信。
  2. 带有填充的 JSON( JSONP )。
  3. 将远程服务器嵌入 iframe 中,并通过 fragment 或 window.name 进行通信,请参见此处

这些棘手的方式或多或少都存在一些问题,例如,如果开发人员只是简单地 “评估”,JSONP 可能会导致安全漏洞;以及上面的#3,尽管它起作用了,但两个域之间应该建立严格的契约,既不灵活也不优雅恕我直言:)

W3C 引入了跨域资源共享(CORS)作为标准解决方案,以提供安全,灵活和推荐的标准方式来解决此问题。

机制

从较高的层次上,我们可以简单地认为 CORS 是域 A 的客户端 AJAX 调用与域 B 上托管的页面之间的合同,典型的跨域请求 / 响应为:

DomainA AJAX 请求标头

Host DomainB.com
User-Agent Mozilla/5.0 (Windows NT 6.1; WOW64; rv:2.0) Gecko/20100101 Firefox/4.0
Accept text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8,application/json
Accept-Language en-us;
Accept-Encoding gzip, deflate
Keep-Alive 115
Origin http://DomainA.com

DomainB 响应标头

Cache-Control private
Content-Type application/json; charset=utf-8
Access-Control-Allow-Origin DomainA.com
Content-Length 87
Proxy-Connection Keep-Alive
Connection Keep-Alive

我在上面标记的蓝色部分是核心事实,“Origin” 请求标头 “指示跨域请求或预检请求的来源”,“Access-Control-Allow-Origin” 响应标头指示此页面允许来自远程请求 DomainA(如果值为 *,则表示允许来自任何域的远程请求)。

正如我上面提到的,W3 建议浏览器在提交实际的跨域 HTTP 请求之前实现一个 “ 预检请求 ”,简而言之,它是一个 HTTP OPTIONS请求:

OPTIONS DomainB.com/foo.aspx HTTP/1.1

如果 foo.aspx 支持 OPTIONS HTTP 动词,则它可能返回如下响应:

HTTP/1.1 200 OK
Date: Wed, 01 Mar 2011 15:38:19 GMT
Access-Control-Allow-Origin: http://DomainA.com
Access-Control-Allow-Methods: POST, GET, OPTIONS, HEAD
Access-Control-Allow-Headers: X-Requested-With
Access-Control-Max-Age: 1728000
Connection: Keep-Alive
Content-Type: application/json

仅当响应包含 “Access-Control-Allow-Origin” 并且其值为 “*” 或包含提交 CORS 请求的域时,通过满足此强制性条件,浏览器将提交实际的跨域请求,并缓存结果在 “ 预检结果缓存 ” 中。

我三年前写了关于 CORS 的博客: AJAX 跨源 HTTP 请求

问题有点老,无法回答,但是我将其发布,以供将来对该问题的任何参考。

根据 Mozilla 开发人员网络文章,

当资源从与第一个资源本身所服务的域或端口不同的域或端口请求资源时,它会发出跨域 HTTP 请求

在此处输入图片说明

从所服务的HTML 页面 http://domain-a.com使得一个<img>的 SRC 请求http://domain-b.com/image.jpg
如今,网络上的许多页面从不同的域加载资源,例如CSS 样式表图像脚本 (因此应该很酷)。

原产地政策

出于安全原因,浏览器限制了从脚本内部发起的 跨域 HTTP请求。
例如, XMLHttpRequestFetch遵循同源策略
因此,使用XMLHttpRequestFetch的 Web 应用程序只能向其自己的 domain发出HTTP 请求

跨域资源共享(CORS)

为了改善 Web 应用程序,开发人员要求浏览器供应商允许跨域请求。

跨域资源共享(CORS)机制为 Web 服务器提供了跨域访问控制 ,可实现安全的跨域数据传输。
现代浏览器在API 容器 (例如XMLHttpRequestFetch使用CORS来减轻跨源 HTTP 请求的风险。

CORS 的工作方式( Access-Control-Allow-Origin标头)

维基百科

CORS 标准描述了新的 HTTP 标头,这些标头为浏览器和服务器提供了一种仅在获得许可时才请求远程 URL 的方法。

尽管服务器可以执行一些验证和授权, 但是浏览器通常有责任支持这些标头并遵守其施加的限制。

  1. 浏览器发送带有Origin HTTP标头的OPTIONS请求。

    此标头的值是为父页面提供服务的域。当来自http://www.example.com的页面尝试访问service.example.com的用户数据时,以下请求标头将发送到service.example.com

    来源: http//www.example.com

  2. service.example.com的服务器可能会响应:

    • 响应中的Access-Control-Allow-Origin (ACAO)标头,指示允许哪些原始站点。
      例如:

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

    • 如果服务器不允许跨域请求,则显示错误页面

    • 具有通配符的Access-Control-Allow-Origin (ACAO)标头,允许所有域:

      Access-Control-Allow-Origin: *

每当我开始考虑 CORS 时,就像您在问题中所描述的那样,我对哪个网站托管标头的直觉是不正确的。对我来说,考虑同一个原产地政策的目的很有帮助。

相同来源策略的目的是保护您免受 siteA.com 上恶意 Java 脚本的访问,以访问您选择仅与 siteB.com 共享的私人信息。如果没有相同的原始策略,由 siteA.com 的作者编写的 JavaScript 可能会使用您对 siteB.com 的身份验证 cookie,使您的浏览器向 siteB.com 发出请求。这样,siteA.com 可以窃取您与 siteB.com 共享的秘密信息。

有时您需要跨域工作,而这正是 CORS 的来源。CORS 使用Access-Control-Allow-Origin标头列出了可信任运行的其他域(domainB.com),从而放宽了 domainA.com 的原始策略。可以与 domainA.com 进行交互的 JavaScript。

要了解哪个域应服务 CORS 标头,请考虑这一点。您访问的恶意网站. com 包含一些 JavaScript,这些 JavaScript 试图向 mybank.com 发出跨域请求。决定是否设置 CORS 标头以放宽相同的源策略,以允许来自恶意网站的 JavaScript 与之交互,应该由 mybank.com(而不是恶意网站)决定。如果 malicous.com 可以设置其自己的 CORS 标头,以允许其自己的 JavaScript 访问 mybank.com,则这将完全废除该原始策略。

我认为直觉不佳的原因是开发网站时的观点。这是我的网站,使用了所有的 JavaScript,因此它没有做任何恶意的事情,应该由来指定我的 JavaScript 可以与哪些其他网站进行交互。实际上,我何时应该在考虑 JavaScript 尝试与其他网站进行哪些交互,我是否应该使用 CORS 允许它们进行交互?

1. 客户端从源地址http:// siteA下载 javascript 代码 MyCode.js。

进行下载的代码 - 您的 html 脚本标记或来自 javascript 的 xhr 或其他内容 - 假设来自http:// siteZ 。并且,当浏览器请求 MyCode.js 时,它将发送一个 Origin:标头, 标明 “ Origin: http:// siteZ ”,因为它可以看到您正在请求 siteA 和 siteZ!= siteA。 (您不能停止或干预。)

2. MyCode.js 的响应标头包含 Access-Control-Allow-Origin: http:// siteB ,我认为这意味着 MyCode.js 被允许对站点 B 进行跨域引用。

没有。这意味着,仅 siteB 被允许执行此请求。因此,您从 siteZ 请求 MyCode.js 时会出错,浏览器通常什么也没有给您。但是,如果使服务器返回 ACAO:siteZ,则将获得 MyCode.js。或者,如果它发送 “*”,那么它将起作用,它将允许所有人进入。或者,如果服务器始终从 Origin:标头发送字符串,但是... 出于安全考虑,如果您担心黑客的话,您的服务器应只允许允许进入这些请求的候选清单中的来源。

然后,MyCode.js 来自 siteA。当它向 siteB 发出请求时,它们都是跨域的,浏览器将发送 Origin:siteA,并且 siteB 必须获取 siteA,将其识别为允许的简短请求者列表,然后发送回 ACAO:siteA。只有这样,浏览器才会让您的脚本获取这些请求的结果。

使用ReactAxios ,将代理链接连接到 URL 并添加标题,如下所示

https://cors-anywhere.herokuapp.com/ + Your API URL

只需添加 Proxy 链接即可,但也会再次为 “无法访问” 引发错误。因此最好添加标题,如下所示。

axios.get(`https://cors-anywhere.herokuapp.com/[YOUR_API_URL]`,{headers: {'Access-Control-Allow-Origin': '*'}})
      .then(response => console.log(response:data);
  }

如果您只想测试跨浏览器阻止您的请求的跨域应用程序,则可以在不安全的模式下打开浏览器并测试您的应用程序,而无需更改代码,也不会使代码不安全。在 MAC OS 中,您可以从终端行执行此操作:

open -a Google\ Chrome --args --disable-web-security --user-data-dir

如果您使用的是 PHP,请尝试在 php 文件的开头添加以下代码:

如果使用本地主机,请尝试以下操作:

header("Access-Control-Allow-Origin: *");

如果使用服务器等外部域,请尝试以下操作:

header("Access-Control-Allow-Origin: http://www.website.com");

我使用 Express 4 和 Node 7.4 和 angular,我遇到了同样的问题,我需要帮助:
a)服务器端:在文件 app.js 中,我为所有响应提供标头,例如:

app.use(function(req, res, next) {  
      res.header('Access-Control-Allow-Origin', req.headers.origin);
      res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
      next();
 });

这必须在所有路由器之前
我看到了很多添加的标题:

res.header("Access-Control-Allow-Headers","*");
res.header('Access-Control-Allow-Credentials', true);
res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE');

但我不需要
b)客户端:在发送 ajax 中,您需要添加:“withCredentials:true”,例如:

$http({
     method: 'POST',
     url: 'url, 
     withCredentials: true,
     data : {}
   }).then(function(response){
        // code  
   }, function (response) {
         // code 
   });

祝好运。

在 Python 中,我一直使用Flask-CORS取得了巨大的成功。它使与 CORS 的交往变得超级轻松而轻松。我从下面的库文档中添加了一些代码。

安装:

$ pip install -U flask-cors

一个简单的示例,它允许所有路由上的所有域都使用 CORS:

from flask import Flask
from flask_cors import CORS

app = Flask(__name__)
CORS(app)

@app.route("/")
def helloWorld():
  return "Hello, cross-origin-world!"

有关更具体的示例,请参阅文档。我已经使用上面的简单示例解决了正在构建的离子应用程序中的 CORS 问题,该应用程序必须访问单独的烧瓶服务器。