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

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

  1. 客户端从http://siteA-的起源下载JavaScript代码MyCode.js。
  2. MyCode.js的响应头包含访问控制允许起源:#0,我认为这意味着允许MyCode.js对站点B进行跨源引用。
  3. 客户端触发MyCode.js的一些功能,这些功能反过来向http://siteB发出请求,尽管是跨源请求,但应该没问题。

好吧,我错了。它根本不是这样工作的。所以,我读了跨域资源共享并试图读w3c建议中的跨源资源共享

有一件事是肯定的-我仍然不明白我应该如何使用这个标题。

我可以完全控制站点A和站点B。如何使用此标头启用从站点A下载的JavaScript代码以访问站点B上的资源?

注:我不想使用JSONP

2300276 次浏览

访问-控制-允许-起源跨域资源共享报头

当站点A尝试从站点B获取内容时,站点B可以发送一个访问-控制-允许-起源响应标头来告诉浏览器该页面的内容可以被某些来源访问。(起源域,加上方案和端口号。)默认情况下,站点B的页面是无法进入任何其他来源;使用访问-控制-允许-起源标头为特定请求来源的跨源访问打开了一扇门。

对于站点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预检,则浏览器会发送实际请求。

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

OPTIONS /somePage HTTP/1.1Origin: http://siteA.comAccess-Control-Request-Method: PUTAccess-Control-Request-Headers: Content-Type

请注意,Access-Control-Request-MethodAccess-Control-Request-Headers是由浏览器自动添加的;您不需要添加它们。此OPTIONS预检获取成功的响应标头:

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

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

浏览器发送实际请求:

PUT /somePage HTTP/1.1Origin: http://siteA.comContent-Type: application/json
{ "myRequestContent": "JSON is so great" }

服务器发回一个Access-Control-Allow-Origin,就像一个简单的请求一样:

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

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

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

  1. 使用Flash/Silverlight或服务器端作为“代理”进行通信使用远程。
  2. 带有填充的JSON(JSONP)。
  3. 将远程服务器嵌入到ifram中并通过frapad或window.name进行通信,请参阅这里

这些棘手的方法或多或少有一些问题,例如JSONP可能会导致安全漏洞,如果开发人员简单地“评估”它,上面的#3,虽然它有效,但两个域应该在彼此之间建立严格的契约,它既不灵活也不优雅IMHO:)

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

该机制

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

DomainA AJAX请求标头

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

DomainB响应标头

Cache-Control privateContent-Type application/json; charset=utf-8Access-Control-Allow-Origin DomainA.comContent-Length 87Proxy-Connection Keep-AliveConnection Keep-Alive

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

正如我上面提到的,W3建议浏览器在提交实际的Cross-Origin HTTP请求之前实现“飞行前请求”,简而言之,它是一个HTTPOPTIONS请求:

OPTIONS DomainB.com/foo.aspx HTTP/1.1

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

HTTP/1.1 200 OKDate: Wed, 01 Mar 2011 15:38:19 GMTAccess-Control-Allow-Origin: http://DomainA.comAccess-Control-Allow-Methods: POST, GET, OPTIONS, HEADAccess-Control-Allow-Headers: X-Requested-WithAccess-Control-Max-Age: 1728000Connection: Keep-AliveContent-Type: application/json

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

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

1.客户端下载javascript代码MyCode.js从超文本传输协议://siteA-来源。

下载的代码——你的html脚本标签或javascript中的xhr或其他什么——来自超文本传输协议://siteZ。当浏览器请求MyCode.js时,它会发送一个Origin:标头,上面写着“Origin:超文本传输协议://siteZ”,因为它可以看到你正在请求siteA和siteZ!=siteA。(你不能停止或干扰它。)

2.MyCode.js的响应标头包含Access-Control-Allow-Origin:超文本传输协议://siteB,我认为这意味着允许MyCode.js对站点B进行跨源引用。

不。这意味着,只有siteB被允许执行此请求。所以您从siteZ请求MyCode.js会收到错误,浏览器通常不会给您任何信息。但是如果您让您的服务器返回A-C-A-O: siteZ,您将获得MyCode.js。或者如果它发送“*”,这将起作用,这将让每个人都进入。或者如果服务器总是从Origin:标头发送字符串……但是……为了安全起见,如果您害怕黑客,您的服务器应该只允许短名单上的起源,允许提出这些请求。

然后,MyCode.js来自siteA。当它向siteB发出请求时,它们都是跨域的,浏览器发送Origin: siteA,siteB必须接受siteA,识别它在允许请求者的短列表上,并发回A-C-A-O: siteA。只有这样,浏览器才会让你的脚本得到这些请求的结果。

对于跨源共享,设置标题:'Access-Control-Allow-Origin':'*';

php:header('Access-Control-Allow-Origin':'*');

节点:app.use('Access-Control-Allow-Origin':'*');

这将允许共享不同域的内容。

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

如果你正在使用localhost,试试这个:

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

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

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

我和Express.js 4、Node.js 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发送时,您需要添加"凭凭据:true,",例如:

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

如果您只想测试浏览器阻止您的请求的跨域应用程序,那么您可以在不安全模式下打开浏览器并测试您的应用程序,而无需更改代码,也不会使代码不安全。

macOS开始,您可以从终端行执行此操作:

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

根据这个 Mozilla开发者网络文章,

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

在此输入图片描述

来自http://domain-a.com超文本标记语言页http://domain-b.com/image.jpg发出<img> src请求。今天网络上的许多页面从不同的域加载CSS样式表图像脚本等资源(因此应该很酷)。

同源政策

出于安全原因,浏览器限制跨域超文本传输协议请求从脚本中启动。例如,XMLHttpRequestFetch跟随主域同源策略。因此,使用XMLHttpRequestFetch的Web应用程序只能使http请求变为自己的领域

跨域资源共享(CORS)

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

跨域资源共享(CORS)机制提供了Web服务器跨域访问控制,它支持安全的跨域数据传输。现代浏览器在API容器(例如XMLHttpRequestfetch)中使用CORS来降低跨源HTTP请求的风险。

CORS如何工作(访问-控制-允许-起源头)

维基百科

CORS标准描述了新的HTTP标头,它为浏览器和服务器提供了一种仅在他们有权限时请求远程URL的方法。

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

示例

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

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

Origin: http://www.example.com
  1. 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: *

使用ReactAxios,将代理链接加入URL并添加标头,如下所示:

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

只需添加代理链接即可,但它也可能再次抛出无访问权限错误。因此,最好添加一个标题,如下所示。

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

警告:不能用于生产

这只是一个快速的解决方案。如果你正在努力解决为什么你无法得到回应,你可以可以使用这个。不是生产的最佳答案。

在Python中,我一直在使用Flask-CORS并取得了巨大的成功。它使处理CORS变得非常容易和无痛。我从下面的库留档中添加了一些代码。

安装:

pip install -U flask-cors

允许CORS用于所有路由上的所有域的简单示例:

from flask import Flaskfrom flask_cors import CORS
app = Flask(__name__)CORS(app)
@app.route("/")def helloWorld():return "Hello, cross-origin-world!"

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

每当我开始考虑CORS时,我对哪个站点托管标题的直觉是不正确的,就像你在问题中描述的那样。对我来说,思考主域同源策略的目的很有帮助。

主域同源策略的目的是保护您免受恶意JavaScriptsiteA.com访问您选择仅与siteB.com.共享的私人信息如果没有主域同源策略,siteA.com作者编写的JavaScript可能会让您的浏览器使用您的身份验证cookie请求siteB.comsiteB.com.通过这种方式,siteA.com可以窃取您与siteB.com.共享的秘密信息

CORS放宽了siteB.com的主域同源策略,使用Access-Control-Allow-Origin标头列出受信任运行JavaScript的其他域(siteA.com),这些域可以与siteB.com.交互

要了解哪个域应该为CORS标头提供服务,请考虑以下内容。您访问malicious.com,其中包含一些试图向mybank.com发出跨域请求的JavaScript。应该由mybank.com而不是malicious.com来决定是否设置放松主域同源策略的CORS标头,允许malicious.com的JavaScript与它交互。如果malicous.com可以设置自己的CORS标头,允许自己的JavaScript访问mybank.com,这将完全取消主域同源策略。

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

只需将以下代码粘贴到web.config文件中。

注意,您必须在<system.webServer>标签下粘贴以下代码

<httpProtocol><customHeaders><add name="Access-Control-Allow-Origin" value="*" /><add name="Access-Control-Allow-Headers" value="Content-Type" /><add name="Access-Control-Allow-Methods" value="GET, POST, PUT, DELETE, OPTIONS" /></customHeaders></httpProtocol>

Access-Control-Allow-Origin响应标头指示是否响应可以与来自给定来源的请求代码共享。

Header type                 Response header-------------------------------------------Forbidden header name       no

一个响应,告诉浏览器允许来自任何来源的代码访问访问资源将包括以下内容:

Access-Control-Allow-Origin: *

有关更多信息,请访问访问-控制-允许-起源

根据我自己的经验,很难找到一个简单的解释为什么CORS甚至是一个问题。

一旦你理解了为什么它在那里,标题和讨论就会变得更加清晰。


这都是关于cookie的。Cookie按其域存储在客户端上。

一个例子:在您的计算机上,有一个yourbank.com的cookie。也许您的会话在那里。

核心进展:当客户端向服务器发出请求时,它会发送存储在该请求域下的cookie。

您在浏览器上登录到yourbank.com。您请求查看您的所有帐户,并向yourbank.com发送cookie。yourbank.com接收一堆cookie并发回其响应(您的帐户)。

如果另一个客户端向服务器发出交叉起源请求,则会像以前一样发送这些cookie。

你浏览到malicious.com。恶意向不同的银行发出一堆请求,包括yourbank.com

由于cookie按预期进行了验证,因此服务器将授权响应。

这些cookie被收集起来并发送-现在,malicious.com有来自yourbank的响应。

哎呀。


现在,一些问题和答案变得显而易见:

  • “我们为什么不阻止浏览器这样做呢?”是的。那是CORS。

  • "我们如何绕过它?"让服务器告诉请求CORS正常。

nginx和apache

作为阿普西勒的回答的补充,我想添加一个wiki图,它显示请求是否简单(以及OPTIONS飞行前请求是否发送)

在此输入图片描述

对于一个简单的请求(例如,热链接图像),您不需要更改服务器配置文件,但您可以在应用程序中添加标头(托管在服务器上,例如,在php中),就像Melvin Guerrero在他的回答中提到的那样-但记住:如果您在服务器(配置)中添加完整的CORS标头,同时您允许在应用程序中使用简单的CORS(例如,PHP),这根本不起作用。

以下是两个流行服务器的配置:

  • 打开Nginx上的CORSnginx.conf文件)

    location ~ ^/index\.php(/|$) {...add_header 'Access-Control-Allow-Origin' "$http_origin" always; # if you change "$http_origin" to "*" you shoud get same result - allow all domain to CORS (but better change it to your particular domain)add_header 'Access-Control-Allow-Credentials' 'true' always;if ($request_method = OPTIONS) {add_header 'Access-Control-Allow-Origin' "$http_origin"; # DO NOT remove THIS LINES (doubled with outside 'if' above)add_header 'Access-Control-Allow-Credentials' 'true';add_header 'Access-Control-Max-Age' 1728000; # cache preflight value for 20 daysadd_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';  # arbitrary methodsadd_header 'Access-Control-Allow-Headers' 'My-First-Header,My-Second-Header,Authorization,Content-Type,Accept,Origin'; # arbitrary headersadd_header 'Content-Length' 0;add_header 'Content-Type' 'text/plain charset=UTF-8';return 204;}}

  • 打开Apache上的CORS. htaccess文件)

    # ------------------------------------------------------------------------------# | Cross-domain Ajax requests                                                 |# ------------------------------------------------------------------------------
    # Enable cross-origin Ajax requests.# http://code.google.com/p/html5security/wiki/CrossOriginRequestSecurity# http://enable-cors.org/
    # change * (allow any domain) below to your domainHeader set Access-Control-Allow-Origin "*"Header always set Access-Control-Allow-Methods "POST, GET, OPTIONS, DELETE, PUT"Header always set Access-Control-Allow-Headers "My-First-Header,My-Second-Header,Authorization, content-type, csrf-token"Header always set Access-Control-Allow-Credentials "true"

说明:只是测试的临时解决方案

对于那些无法控制Options 405 Method Not Allowed后端的人,这里有一个Chrome浏览器的解决方案。

在命令行中执行:

"C:\Program Files (x86)\Google\Chrome\Application\chrome.exe" --disable-web-security --user-data-dir="path_to_profile"

示例:

"C:\Program Files (x86)\Google\Chrome\Application\chrome.exe" --disable-web-security --user-data-dir="C:\Users\vital\AppData\Local\Google\Chrome\User Data\Profile 2"

我无法在后端服务器上配置它,但通过浏览器中的这些扩展,它可以为我工作:

对于Firefox:

CORS无处不在

对于GoogleChrome:

允许CORS: Access-Control-Allow-Origin

注意:CORS适用于我的以下配置:

允许CORS选项

CORS到处选项

for. NET Core 3.1 API with Angular

Startup.cs:添加CORS

    //SERVICESpublic void ConfigureServices(IServiceCollection services){
//CORS (Cross Origin Resource Sharing)//=====================================services.AddCors();}
//MIDDLEWARESpublic void Configure(IApplicationBuilder app, IWebHostEnvironment env){app.UseRouting();
//ORDER: CORS -> Authentication -> Authorization)//CORS (Cross Origin Resource Sharing)//=====================================app.UseCors(x=>x.AllowAnyHeader().AllowAnyMethod().WithOrigins("http://localhost:4200"));
app.UseHttpsRedirection();}}

控制器:为授权控制器启用CORS

 //Authorize all methods inside this controller[Authorize][EnableCors()]public class UsersController : ControllerBase{//ActionMethods}