提问人:mark 提问时间:5/17/2012 最后编辑:Peter Mortensenmark 更新时间:11/8/2023 访问量:2498009
“Access-Control-Allow-Origin”标头如何工作?
How does the 'Access-Control-Allow-Origin' header work?
问:
显然,我完全误解了它的语义。我想到了这样的事情:
- 客户端从 - 源下载 JavaScript 代码 MyCode.js。
http://siteA
- MyCode.js 的响应标头包含 Access-Control-Allow-Origin:
http://siteB
,我认为这意味着 MyCode.js 被允许对站点 B 进行跨域引用。 - 客户端触发 MyCode.js 的某些功能,这些功能反过来向 发出请求,尽管是跨域请求,这应该没问题。
http://siteB
好吧,我错了。它根本不像这样工作。因此,我已经阅读了跨域资源共享,并尝试阅读 w3c 推荐中的跨域资源共享。
有一件事是肯定的 - 我仍然不明白我应该如何使用这个标题。
我可以完全控制站点 A 和站点 B。如何使从站点 A 下载的 JavaScript 代码能够使用此标头访问站点 B 上的资源?
P.S.:我不想使用JSONP。
答:
Access-Control-Allow-Origin 是一个 CORS(跨域资源共享)标头。
当站点 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 访问此页面,则浏览器将触发 的事件,并拒绝向请求的 JavaScript 代码提供响应数据。XMLHttpRequest
error
非简单请求
网络级别上发生的情况可能比上面解释的要复杂一些。如果请求是“非简单”请求,则浏览器首先发送一个无数据的“预检”OPTIONS请求,以验证服务器是否接受该请求。当其中一个(或两个)使用以下命令时,请求是非简单的:
- 除 GET 或 POST 以外的 HTTP 动词(例如 PUT、DELETE);
- 非简单请求标头;唯一简单的请求标头是:
Accept
;Accept-Language
;Content-Language
;Content-Type
(仅当其值为 、 或 时,这才简单。application/x-www-form-urlencoded
multipart/form-data
text/plain
如果服务器使用与非简单动词和/或非简单标头匹配的适当响应标头(对于非简单标头,对于非简单动词)响应 OPTIONS 预检,则浏览器将发送实际请求。Access-Control-Allow-Headers
Access-Control-Allow-Methods
假设站点 A 想要发送 的 PUT 请求,其非简单值为 ,浏览器将首先发送预检请求:/somePage
Content-Type
application/json
OPTIONS /somePage HTTP/1.1
Origin: http://siteA.com
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: Content-Type
请注意,和是由浏览器自动添加的;您无需添加它们。此 OPTIONS 预检获取成功的响应标头:Access-Control-Request-Method
Access-Control-Request-Headers
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。
评论
Access-Control-Allow-Origin
Access-Control-Allow-Origin
*
Origin
跨域资源共享 - (又名跨域 AJAX 请求)是大多数 Web 开发人员可能会遇到的问题,根据 Same-Origin-Policy,浏览器将客户端 JavaScript 限制在安全沙箱中,通常 JS 无法直接与来自不同域的远程服务器通信。过去,开发人员创造了许多棘手的方法来实现跨域资源请求,最常用的方法有:CORS
- 使用 Flash/Silverlight 或服务器端作为“代理”进行通信 带遥控器。
- 带填充的 JSON (JSONP)。
- 将远程服务器嵌入 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请求的域时,通过满足此强制条件,浏览器才会提交实际的跨域请求,并将结果缓存在“Preflight-Result-Cache”中。
三年前,我写了一篇关于 CORS 的博客:AJAX 跨域 HTTP 请求
评论
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 的请求反而会出错,浏览器通常不会给您任何信息。但是,如果您让服务器返回 A-C-A-O: siteZ,您将获得 MyCode.js 。或者,如果它发送“*”,那将起作用,这将使每个人都进入。或者,如果服务器始终从 Origin: 标头发送字符串...但。。。为了安全起见,如果您害怕黑客,您的服务器应该只允许候选名单上的来源,这些来源被允许发出这些请求。
然后,MyCode.js 来自 siteA。当它向 siteB 发出请求时,它们都是跨域的,浏览器发送 Origin: siteA,siteB 必须获取 siteA,识别它在允许的请求者的短列表中,并发回 A-C-A-O: siteA。只有这样,浏览器才会让您的脚本获取这些请求的结果。
对于跨域共享,请设置 header:'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 发送时,需要添加 “withCredentials: true”,例如:
$http({
method: 'POST',
url: 'url',
withCredentials: true,
data : {}
}).then(function(response){
// Code
}, function (response) {
// Code
});
评论
res.header('Access-Control-Allow-Origin', req.headers.origin);
与res.header('Access-Control-Allow-Origin', '*');
*
而且不一样...... 使用凭据时不允许。req.headers.origin
*
如果您只想测试浏览器阻止您的请求的跨域应用程序,那么您可以在不安全模式下打开浏览器并测试您的应用程序,而无需更改您的代码,也不会使您的代码不安全。
在 macOS 中,您可以从终端行执行此操作:
open -a Google\ Chrome --args --disable-web-security --user-data-dir
根据这篇Mozilla开发者网络文章,
当资源从与第一个资源本身所服务的域或端口不同的域或端口请求资源时,它会发出跨域 HTTP 请求。
提供的 HTML 页面发出 src 请求。
如今,网络上的许多页面都从不同的域加载了CSS样式表、图像和脚本等资源(因此它应该很酷)。http://domain-a.com
<img>
http://domain-b.com/image.jpg
同源策略
出于安全原因,浏览器会限制从脚本中发起的跨域 HTTP 请求。
例如,并遵循同源策略。
因此,Web 应用程序使用 或只能向自己的域发出 HTTP 请求。XMLHttpRequest
Fetch
XMLHttpRequest
Fetch
跨域资源共享 (CORS)
为了改进 Web 应用程序,开发人员要求浏览器供应商允许跨域请求。
跨域资源共享 (CORS) 机制为 Web 服务器提供跨域访问控制,从而实现安全的跨域数据传输。
现代浏览器在 API 容器(例如 OR)中使用 CORS 来降低跨域 HTTP 请求的风险。XMLHttpRequest
fetch
CORS 的工作原理(Access-Control-Allow-Origin 标头)
维基百科:
CORS 标准描述了新的 HTTP 标头,它为浏览器和服务器提供了一种仅在具有权限时请求远程 URL 的方法。
尽管服务器可以执行一些验证和授权,但通常由浏览器负责支持这些标头并遵守它们施加的限制。
例
- 浏览器发送带有标头的请求。
OPTIONS
Origin HTTP
此标头的值是为父页面提供服务的域。 当页面尝试访问用户的 数据中,以下请求标头将是 发送到 :
http://www.example.com
service.example.com
service.example.com
lang-none Origin: http://www.example.com
- 服务器可以响应:
service.example.com
响应中的 (ACAO) 标头,指示允许哪些源站点。例如:
Access-Control-Allow-Origin
Access-Control-Allow-Origin: http://www.example.com
如果服务器不允许跨域请求,则会出现错误页面
带有通配符的 (ACAO) 标头,允许所有域:
Access-Control-Allow-Origin
Access-Control-Allow-Origin: *
评论
Access-Control-Allow-Origin:null
Access-Control-Allow-Origin
Access-Control-Allow-Origin: *
http-config
htaccess
使用 React 和 Axios,将代理链接加入到 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 Flask
from flask_cors import CORS
app = Flask(__name__)
CORS(app)
@app.route("/")
def helloWorld():
return "Hello, cross-origin-world!"
有关更具体的示例,请参阅文档。我使用上面的简单示例来解决我正在构建的 Ionic 应用程序中的 CORS 问题,该应用程序必须访问单独的 flask 服务器。
每当我开始考虑 CORS 时,我对哪个站点托管标头的直觉都是不正确的,就像您在问题中描述的那样。对我来说,思考同源策略的目的会有所帮助。
同源策略的目的是在访问您选择仅与 siteB.com 共享的私人信息 siteA.com 保护您免受恶意 JavaScript 的侵害。如果没有同源策略,siteA.com 的作者编写的 JavaScript 可能会让您的浏览器向 siteB.com 发出请求,使用您的身份验证 Cookie 进行 siteB.com。通过这种方式,siteA.com 可以窃取您与 siteB.com 共享的秘密信息。
有时您需要跨域工作,这就是 CORS 的用武之地。CORS 放宽了 siteB.com 的同源策略,使用标头列出受信任运行可与 siteB.com 交互的 JavaScript 的其他域 (siteA.com)。Access-Control-Allow-Origin
若要了解哪个域应提供 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: *
有关更多信息,请访问 Access-Control-Allow-Origin...
评论
根据我自己的经验,很难找到一个简单的解释,为什么CORS甚至是一个问题。
一旦你明白了它为什么在那里,标题和讨论就会变得更加清晰。我会用几行来试一试。
这一切都与cookie有关。Cookie 按其域存储在客户端上。
一个示例故事:在您的计算机上,有一个 cookie 。也许你的会话就在那里。
yourbank.com
眼:当客户端向服务器发出请求时,它将为该请求发送存储在域下的 cookie。
您已在浏览器上登录到 。您请求查看您的所有帐户,并发送 cookie 以用于 。 接收一堆 cookie 并发回其响应(您的帐户)。
yourbank.com
yourbank.com
yourbank.com
如果另一个客户端向服务器发出跨域请求,则会像以前一样发送这些 cookie。呸。
浏览到 。恶意向不同的银行发出一堆请求,包括 .
malicious.com
yourbank.com
由于 cookie 按预期进行验证,因此服务器将授权响应。
这些 cookie 被收集起来并发送 - 现在,有来自 .
malicious.com
yourbank
哎呀。
所以现在,一些问题和答案变得显而易见:
“我们为什么不直接阻止浏览器这样做呢?”是的。这就是CORS。
“我们该如何绕过它?”让服务器告诉请求 CORS 正常。
评论
yourbank.com
malicious.com
malicious.com
yourbank.com
yourbank.com
malicious.com
Nginx 和 Apache
作为对apsiller答案的补充,我想添加一个wiki图表,该图表显示请求何时简单或不简单(以及OPTIONS预检请求是否发送)
对于一个简单的请求(例如,热链接图像),你不需要更改你的服务器配置文件,但你可以在应用程序中添加标头(托管在服务器上,例如,在PHP中),就像Melvin Guerrero在他的回答中提到的那样 - 但请记住:如果你在服务器(配置)中添加完整的CORS标头,同时允许在应用程序中使用简单的CORS(例如, PHP),这根本行不通。
以下是两个常用服务器的配置:
在 Nginx 上打开 CORS(nginx.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 days add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS'; # arbitrary methods add_header 'Access-Control-Allow-Headers' 'My-First-Header,My-Second-Header,Authorization,Content-Type,Accept,Origin'; # arbitrary headers add_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 domain Header 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"
注意:只是测试的临时解决方案
对于那些无法控制后端的人,这是Chrome浏览器的解决方法。Options 405 Method Not Allowed
在命令行中执行:
"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:
对于 Google Chrome:
允许 CORS:Access-Control-Allow-Origin
注意:CORS适用于以下配置:
对于使用 Angular 的 .NET Core 3.1 API
启动.cs:添加CORS
//SERVICES
public void ConfigureServices(IServiceCollection services){
//CORS (Cross Origin Resource Sharing)
//=====================================
services.AddCors();
}
//MIDDLEWARES
public 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
}
这很奇怪,但可能会发生 CORS 问题,因为您正在尝试通过客户端 ajax 从作为前端基本库的 react、angular、jquery 应用程序请求。
您必须从后端应用程序请求。
您正在尝试从前端 API 发出请求,但您尝试使用的 API 期望从后端应用程序发出此请求,并且它永远不会接受客户端请求。
我尝试了来自前端的请求,但没有奏效,然后我使用 curl 发出了相同的请求,并且成功了。
评论
http://siteA/MyCode.js