转载请备注来源: 《CORS跨资源共享详解》 | shuwoom.com

CORS简介

1.CORS是什么

CORS的全称叫Cross Origin resource sharing,是一种机制,它允许web服务器进行跨域访问控制,即让运行在一个orgin上的网站去访问来自不同源服务器(不同的域名、协议或端口)上指定的资源。

例如:http://a.com站点上的页面通过<img>的src请求http://b.com/logo.jpg资源,由于是访问非同源的域名,所以浏览器会发起一个跨域的HTTP请求。

备注:这里同源的定义是:域名、协议和端口相同,才认为是同源

2.为什么会有CORS问题

那么为什么会有这种同源的策略限制呢?主要是出于安全原因考虑,浏览器限制从脚本(如JavaScript)中发起的跨域HTTP请求,即JavaScript或Cookie只能访问同源下的内容。否则可能会出现下图的CSRF(Cross-site request forgery)跨站请求伪造问题:

csrf夸张请求伪造

如上图,那么用户在成功登陆网站并生成有效cookie的情况下,如果通过在受信网站A上一段JavaScript脚本访问恶意网站B上的恶意代码,这时候在网站A上通过脚本访问网站B是一个跨站的HTTP请求,加入没有同源策略的限制,那么这时候浏览器就会允许这个跨站的HTTP请求发生,这就导致执行了恶意代码,从而窃取了用户在受信网站A上的敏感信息。而现在,由于同源策略的限制,这种跨站请求浏览器是不允许的,所以网站A上通过脚本访问恶意网站B是被禁止的,这样就保证了用户的信息的安全性。

3.CORS怎样验证

上面,我们知道出于安全原因,浏览器默认对于跨域HTTP请求采用的是同源策略,即限制只能访问同源下的内容。那么,如果一个网站真的有需求要访问其他网站上的资源要怎么办呢?

W3C就推荐了一种跨站访问的验证机制,也就是今天要说的CORS跨资源共享。这种机制允许web服务器支持跨站访问控制,使得跨站数据传输更加安全。CORS验证机制需要客户端和服务端协同处理,而客户端上的操作浏览器会自动完成,所以CORS验证的关键是服务器,只要服务器实现了CORS验证,就可以支持跨站通信。

4.两种请求

浏览器将CORS请求分为两类:简单请求和非简单请求,两者区别是简单请求不会触发CORS预检请求。只要同时满足以下两类条件,就属于简单请求。

(1)请求方法是以下三种之一

  • HEAD
  • GET
  • POST

(2)HTTP的头信息不超出以下几个字段:

  • Accept
  • Accept-Language
  • Content-Language
  • Last-Event-ID
  • Content-Type:只限于3个值application/x-www-form-urlencoded、multipart/form-data、text/plain

凡是不同时满足上面两个条件的,就属于非简单请求,两种请求的处理流程是不一样的,后者比前者多了个预检请求流程。

预检请求即浏览器在进行正式通信之前,增加一次HTTP查询请求,这个请求就称为预检请求。

这时候浏览器会先询问服务器,当前网页所在的域名是否在服务端的许可名单之中、可以使用哪些HTTP请求方法、可以使用哪些头部信息字段。只有浏览器得到肯定回复后,才会发出正式的请求,否则就会报错。

简单CORS请求

1.客户端请求(Request)

对于简单请求,浏览器直接发出CORS请求,同时会自动在请求头部(request header)中增加Origin字段。例如:网站http://a.com的网页想要访问http://b.com上的资源,则客户端和服务端之间使用CORS Header字段来处理跨域权限:

cors简单请求

请求头部中Origin表明请求来源于http://a.com网站

2.服务端响应(Response)

服务端返回的Access-Control-Allow-Origin: * 表明网站b.com的资源允许被任意的外域访问,如果服务端要限制外域访问,只允许http://a.com访问,则该首部字段内容如下: Access-Control-Allow-Origin: http://a.com。

简单的CORS请求只需要Origin和Access-Control-Allow-Origin就可以完成。

非简单CORS请求

上面,我们介绍了简单的CORS请求,这次,我们介绍带预检请求的非简单CORS请求。同样的我们采用上面的demo,只不过客户端请求header稍作修改,如下:

非简单cors请求分为两部分,分别是预检请求和正式请求。

1.预检请求

(1)客户端请求(Request)

浏览器发现,该请求是非简单请求时,就会自动发出一个预检请求,要求服务器确认该请求。请求信息如上图:

采用的方法是OPTIONS,这个请求方法主要用来询问用的。

Origin表示自己来自哪个源;

Access-Control-Request-Method表示自己即用采用的HTTP请求方法;

Access-Control-Request-Headers指定浏览器请求会额外发送其他头部信息字段。

(2)服务端响应(Response)

服务器在收到预检请求后,就会去检查OriginAccess-Control-Request-Method和Access-Control-Request-Headers ,确认允许跨域访问后,才做出回应。

Access-Control-Allow-Origin响应头部字段告诉浏览器允许跨域的外源;

Access-Control-Allow-Methods响应头部字段则告诉浏览器客户端,服务器支持的HTTP请求方法;

Access-Control-Allow-Headers响应头部字段表明服务器允许请求中携带 X-PINGOTHER和Content-Type字段 。

2.正式请求

一旦服务器通过了预检请求后,以后每次浏览器正常的CORS请求,就跟简单请求一样,会有一个Origin头部信息字段。服务器的响应也会返回Access-Control-Allow-Origin头部信息字段。

带Cookie的CORS请求

上面将的都是不带cookie情况下的cors请求,那么如果请求中要带cookie凭证的话又会有什么不同呢?这里需要客户端(浏览器)和服务端一起协同配合才能够发送带cookie的cors请求。

1.客户端设置

默认情况下,对于跨域的XMLHttpRequest或Fetch请求,浏览器是不会发送cookie信息的,如果JavaScript要发送cookie信息,在浏览器端需要设置XMLHttpRequest的某个特殊标志位。

需要将XMLHttpRequest的withCredentials标志设置为true,从而允许向服务器发送cookie信息。代码如下:

var invocation = new XMLHttpRequest();
var url = 'http://b.com/doc';
    
function callOtherDomain(){
  if(invocation) {
    invocation.open('GET', url, true);
    invocation.withCredentials = true;
    invocation.onreadystatechange = handler;
    invocation.send(); 
  }
}

2.服务端设置

如果服务端的响应头部中未设置如下响应头部,则浏览器将不会把响应的内容返回个请求的发送者。

Access-Control-Allow-Credentials: true 

这里需要注意的是,附带cookie信息的cors请求,服务器不能将Access-Control-Allow-Origin的值设置为”*”通配符,否则请求将会失败。而将
Access-Control-Allow-Origin设置为http://a.com(即设置成请求中的Origin源即可)请求将执行成功。

HTTP请求header字段

Origin: <origin>

当浏览器检测到HTTP跨域请求时,会自动添加Origin字段信息,origin参数为源站URI。例如我在网站a.com上跨站访问b.com,那么浏览器添加的origin信息位http://a.com。

Access-Control-Request-Method: <method>

Access-Control-Request-Method字段用于预检请求,作用是将实际请求所使用的HTTP方法告诉服务器,如GET、POST、OPTION等。

Access-Control-Request-Headers: <field-name>[, <field-name>]*

Access-Control-Request-Headers同样用于预检请求,作用是将实际请求所携带的header字段告诉服务器

HTTP响应header字段

Access-Control-Allow-Origin: <origin> | *

Access-Control-Allow-Origin表示服务器允许的访问该资源的外域URI。如果不需要携带cookie请求,服务器可以指定该字段为通配符”*”,表示允许所有域的请求。

例如:Access-Control-Allow-Origin: b.com表示服务器允许访问该资源的外域只有b.com网站,其他外域网站不允许。

Access-Control-Allow-Credentials: true

当浏览器的credentials设置为true时,Access-Control-Allow-Credentials头指定了浏览器是否允许读取response的内容。这个会在带cookie的cors请求中用到。

Access-Control-Allow-Methods: <method>[, <method>]*

Access-Control-Allow-Methods字段用于预检请求,表示服务端允许的请求HTTP请求方法。

Access-Control-Allow-Headers: <field-name>[, <field-name>]*

Access-Control-Allow-Headers首部字段用于预检请求的响应,其指明了服务器允许请求中携带的首部字段。

转载请备注来源: 《CORS跨资源共享详解》 | shuwoom.com

打赏

发表评论

电子邮件地址不会被公开。