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

客户端从下载JavaScript代码MyCode.jshttp://siteA-原产地。MyCode.js的响应标头包含Access Control Allow Origin:http://siteB,我认为这意味着允许MyCode.js对站点B进行跨源引用。客户端触发MyCode.js的一些功能,进而向http://siteB,这应该可以,尽管是跨源请求。

嗯,我错了。它根本不是这样工作的。所以,我已经阅读了跨源资源共享,并尝试阅读w3c推荐中的跨源资源分享。

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

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

备注:我不想使用JSONP。


访问控制允许源是CORS(跨源资源共享)标头。

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

对于站点B希望站点A访问的每个资源/页面,站点B应为其页面提供响应标题:

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

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

非简单请求

在网络层面发生的事情可能比上面解释的稍微复杂一些。如果请求是“非简单”请求,则浏览器首先发送一个无数据的“预检”OPTIONS请求,以验证服务器是否接受该请求。在以下情况之一(或两者)时,请求是非简单的:

使用GET或POST以外的HTTP动词(例如PUT、DELETE)使用非简单请求头;唯一简单的请求头是:接受接受语言内容语言内容类型(仅当其值为application/x-wwww-form-urlencoded、multipart/form-data或text/plain时才简单)

如果服务器使用与非简单谓词和/或非简单标头匹配的适当响应标头(非简单标头的访问控制允许标头,非简单谓词的访问控制方法)响应OPTIONS预检,则浏览器发送实际请求。

假设站点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

注意,访问控制请求方法和访问控制请求头由浏览器自动添加;您不需要添加它们。此OPTIONS预检获得成功的响应标头:

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

在发送实际请求时(飞行前完成后),行为与处理简单请求的方式相同。换言之,预飞成功的非简单请求被视为简单请求(即,服务器仍然必须再次发送访问控制允许源以获得实际响应)。

浏览器发送实际请求:

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

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

服务器会发回一个“访问控制允许源”,就像对一个简单的请求一样:

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

有关非简单请求的更多信息,请参阅了解CORS上的XMLHttpRequest。


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

使用Flash/SSilverlight或服务器端作为“代理”进行通信使用遥控器。带填充的JSON(JSONP)。在iframe中嵌入远程服务器,并通过fragment或window.name进行通信,请参阅此处。

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

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-原产地。

进行下载的代码-您的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进行跨源引用。

不。这意味着,只有站点B才允许执行此请求。因此,您从siteZ请求MyCode.js时会出现错误,浏览器通常不会提供任何信息。但是如果你让你的服务器返回A-C-A-O:siteZ,你会得到MyCode.js。或者如果它发送'*',这会起作用,这会让所有人都进来。或者如果服务器总是从Origin:header发送字符串。。。但是为了安全起见,如果你害怕黑客,你的服务器应该只允许短名单上的来源,允许他们提出这些请求。

然后,MyCode.js来自siteA。当它向站点B发出请求时,它们都是跨源的,浏览器发送origin:siteA,站点B必须接收站点A,识别它在允许的请求者的短列表中,然后发送A-C-A-O:siteA。只有这样,浏览器才会让脚本获得这些请求的结果。


对于跨源共享,请设置标题:“Access Control Allow origin”:“*”;

Php:header(“访问控制-允许原始”:“*”);

节点: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
});

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

在macOS中,您可以从终端行执行此操作:

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

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

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

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

同源政策

出于安全原因,浏览器限制从脚本中发起的跨源HTTP请求。例如,XMLHttpRequest和Fetch遵循相同的源策略。因此,使用XMLHttpRequest或Fetch的web应用程序只能向自己的域发出HTTP请求。

跨源资源共享(CORS)

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

跨源资源共享(CORS)机制为web服务器提供跨域访问控制,从而实现安全的跨域数据传输。现代浏览器在API容器(如XMLHttpRequest或fetch)中使用CORS来降低跨源HTTP请求的风险。

CORS的工作原理(Access Control Allow Origin标头)

维基百科:

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

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

实例

浏览器发送OPTIONS请求和Origin HTTP头。

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

Origin: http://www.example.com

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

其响应中的访问控制允许源站(ACAO)标头指示允许哪些源站。例如:访问控制允许来源:http://www.example.com如果服务器不允许跨源请求,则显示错误页面带有通配符的访问控制允许来源(ACAO)标头,允许所有域:访问控制允许来源:*


使用React和Axios,将代理链接连接到URL,并添加标头,如下所示:

https://cors-anywhere.herokuapp.com/+您的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问题,该应用程序必须访问单独的烧瓶服务器。


每当我开始思考CORS时,我对哪个站点托管标头的直觉是错误的,正如您在问题中所描述的那样。对我来说,思考同源政策的目的是有帮助的。

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

有时您需要跨域工作,这就是CORS的作用所在。CORS放宽了siteB.com的同源策略,使用Access Control Allow origin标头列出其他受信任运行可与siteB.com交互的JavaScript的域(siteA.com)。

要了解哪个域应该为CORS标头提供服务,请考虑这一点。您访问malious.com,其中包含一些试图向mybank.com发出跨域请求的JavaScript。应该由mybank.com而不是malious.com来决定它是否设置CORS标头,以放宽同源策略,从而允许maliouss.com中的JavaScript与之交互。如果malious.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响应标头指示响应可以与来自给定源的请求代码共享。标头类型响应标头-------------------------------------------禁止的标题名称编号告诉浏览器允许任何来源的代码访问资源将包括以下内容:访问控制允许来源:*

有关详细信息,请访问访问控制允许来源。。。


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

一旦你明白了为什么会出现,标题和讨论就会变得更加清晰。我会在几行字里尝试一下。


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

一个例子:在你的电脑上,有一个你的bank.com的cookie。也许你的会话就在那里。

要点:当客户端向服务器发出请求时,它将发送存储在该请求的域下的cookie。

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

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

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

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

这些饼干被收集起来并一起发送——现在,malious.com收到了您银行的回复。

诶呀


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

“我们为什么不阻止浏览器这样做?”是的。这是CORS。“我们怎么解决?”让服务器告诉请求CORS正常。


Nginx和Apache

作为apsiller答案的补充,我想添加一个wiki图表,显示请求是否简单(OPTIONS航班前请求是否发送)

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

下面是两种常用服务器的配置:

打开Nginx上的CORS(Nginx.conf文件)位置~^/index\.php(/|$){...add_header'访问控制允许来源'“$http_Origin”始终;#如果您将“$http_origin”更改为“*”,您将得到相同的结果-允许所有域使用CORS(但最好将其更改为您的特定域)add_header“访问控制允许凭据”“始终为true”;if($request_method=OPTIONS){add_header'访问控制允许来源'“$http_Origin”;#请勿移除该行(用上面的外部“if”加倍)add_header'访问控制允许凭据''true';add_header'访问控制最大年龄'1728000;#缓存20天的飞行前值add_header'访问控制允许方法''GET,POST,OPTIONS';#任意方法add_header'访问控制允许标头''我的第一个标头,我的第二个标头,授权,内容类型,接受,来源';#任意标头add_header“内容长度”0;add_header“内容类型”“text/plain charset=UTF-8”;返回204;}}在Apache上打开CORS(.htaccess文件)# ------------------------------------------------------------------------------#|跨域Ajax请求|# ------------------------------------------------------------------------------#启用跨源Ajax请求。# http://code.google.com/p/html5security/wiki/CrossOriginRequestSecurity# http://enable-cors.org/#将下面的*(允许任何域)更改为您的域标题集访问控制允许来源“*”标头始终设置访问控制允许方法“POST、GET、OPTIONS、DELETE、PUT”标头始终设置访问控制允许标头“My First Header,My Second Header,Authorization,content type,csrf token”标头始终将访问控制允许凭据设置为“true”


注:仅用于测试的临时解决方案

对于那些无法控制“选项405方法不允许”的后端的人,这里有一个解决方案,用于“颜色”浏览器。

在命令行中执行:

“C:\Program Files(x86)\Google\Chrome\Application\Chrome.exe”--禁用web安全--用户数据dir=“path_to_file”

例子:

“C:\Program Files(x86)\Google\Chrome\Application\Chrome.exe”--禁用web安全--用户数据dir=“C:\Users\vital\AppData\Local\Google\Cherme\user data\Profile 2”


我无法在后端服务器上配置它,但通过浏览器中的这些扩展,它对我很有用:

对于Firefox:

CORS无处不在

对于Google Chrome:

允许CORS:访问控制允许源

注意:CORS适用于此配置:


对于带有Angular的.NET Core 3.1 API

Startup.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期望此请求来自后端应用程序,并且它永远不会接受客户端请求。