跨域

什么是跨域?

所谓跨域,就是网站的协议名(protocol:如http://)、域名(host:如 www.example.com)、端口号(port:例如80,默认端口可以省略)这三者中任意一个不同,网站之间的数据传输或者请求就属于跨域请求了。
这是由于浏览器的同源策略,为了防范跨站脚本的攻击,禁止客户端对不同源的服务器进行跨站调用,但是跨域并非浏览器限制了发起跨站请求,而是跨站请求可以正常发送,但是返回结果被浏览器拦截了。
如果是非同源,共有三种行为受到限制:

  • cookie、localStorage和IndexedDB无法读取
  • DOM无法获得
  • AJAX请求不能发送

既然我们知道了什么是跨域,在实际的开发过程中我们又会无法避免跨域请求,那么怎么解决这个限制呢,其中有一个方法就是CORS。

CORS 跨域资源共享

CORS (Cross-Origin Resource Sharing)跨域资源共享,定义了必须在访问跨域资源时,浏览器与服务器应该如何沟通。
背后的思想是使用自定义的http头部,让服务器能声明哪些来源可以通过浏览器访问该服务器上的资源,从而决定请求或相应是应该成功还是失败。
接下来我们介绍CORS的内部机制

1. 简介

CORS需要浏览器和服务器同时支持。
整个CORS通信过程,都是浏览器自动完成,不需要用户参与,对于开发者来说,CORS通信与同源的AJAX通信没有差别,代码完全一样。
实现CORS通信的关键是服务器,只要服务器实现了CORS接口,就可以实现跨域通信。

2. 两种请求

浏览器将CORS分成两类:简单请求(simple request)和非简单请求(not-so-simple request),只要同时满足以下两大条件(日常开发一般只关注这两点,所以这里只说这两点),就属于简单请求

  1. 请求方法是这三种之一:HEADGETPOST
  2. 只使用了如下的安全首部字段,不得人为设置其他首部字段
  • Accept
  • Accept-Language
  • Content-Language
  • Content-Type :仅限于text/plainmultipart/form-dataapplication/x-www-form-urlencoded三种
  • HTML 头部 header filed 字段:DPRDownloadSave-DataViewport-WidthWidth

凡是不同时满足以上条件,就属于非简单请求

3.简单请求

对于简单请求,浏览器直接发出CORS请求。具体来说,就是在头信息之中,增加一个Origin字段。例如:

GET /cors HTTP/1.1
Origin: http://api.bob.com
Host: api.alice.com
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...

在上面这段头信息中,Origin字段表示本次请求来自哪个源。如果这个源不在服务器许可范围内,会返回一个正常http回应(不包含Access-Control-Allow-Origin字段),浏览器接收后发现没有这个字段,就会抛出错误(这个错误无法通过状态码识别,因为http回应中的状态码有可能是200)。
如果这个源在许可范围内,服务器返回的响应,会多几个头信息字段:

Access-Control-Allow-Origin: http://api.bob.com
Access-Control-Allow-Credentials: true
Access-Control-Expose-Headers: FooBar
Content-Type: text/html; charset=utf-8

在这个头信息中,有三个与CORS请求相关的字段,都以Access-Control-开头。
3.1 Access-Control-Allow-Origin
该字段是必须的。它的值要么是请求时Origin字段的值,要么是一个*,表示接受任意域名的请求。
3.2 Access-Control-Allow-Credentials
该字段可选,表示是否允许发送Cookie。默认情况下,Cookie不包括在CORS请求之中,如果服务器明确许可客户端发送Cookie,就要把这个值设为true;如果不许可,就不要添加这个字段。(因为这个字段的值只能被设为true)
3.3 Access-Control-Expose-Headers
这个字段可选,这个值表示了XMLHttpRequest对象的getResponseHeader()方法能拿到的某个字段,因为这个方法本来是只能拿到6个基本字段(Cache-ControlContent-LanguageContent-TypeExpiresLast-ModifiedPragma)。有了上面这个响应值,getResponseHeader('FooBar')可以返回FooBar字段的值。
withCredentials 属性
如果要把cookie发到服务器,除了3.2中的字段值的设置表示服务器许可,还要开发者必须在 AJAX请求中打开withCredentials 属性

var xhr = new XMLHttpRequest();
xhr.withCredentials = true;

如果不想要浏览器发送cookie,就要设置这个属性值为false。
注意:
如果要发送cookie,3.1的字段值就不能设置为 * 号,必须指定明确的、与网页请求一致的域。同时,Cookie依然遵循同源政策,只有用服务器域名设置的Cookie才会上传,其他域名的Cookie并不会上传,且(跨源)原网页代码中的document.cookie也无法读取服务器域名下的Cookie。

4. 非简单请求

4.1 预检请求
非简单请求是那种需要对服务器有特殊要求的请求,比如请求方法是PUTDELETE,或者Content-Type字段的类型是application/json。这种会在正式通信之前,增加一次HTTP查询请求,称为“预检请求(preflight)”。
浏览器先询问服务器,当前网页所在的域名是否在服务器的许可名单之中,以及可以使用哪些HTTP动词和头信息字段
比如对于下面这样一个非简单请求

var url = 'http://api.alice.com/cors';
var xhr = new XMLHttpRequest();
xhr.open('PUT', url, true);
xhr.setRequestHeader('X-Custom-Header', 'value');
xhr.send();

浏览器会自动发出一个预检请求,预检请求必须使用OPTIONS方法,用来询问,例如对应上面这个非简单请求的预检请求:

OPTIONS /cors HTTP/1.1
Origin: http://api.bob.com
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: X-Custom-Header
Host: api.alice.com
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...

除了Origin字段,"预检"请求的头信息包括两个特殊字段:
(1). Access-Control-Request-Method
必须字段,列出浏览器的CORS请求会用到哪些HTTP方法,这里是PUT
(2). Access-Control-Request-Headers
该字段是一个逗号分隔的字符串,指定浏览器CORS请求会额外发送的头信息字段,这里是X-Custom-Header

4.2 预检请求的回应
服务器收到请求后,检查了楼上三个字段,确认允许跨域请求,就可以做出回应:

HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 01:15:39 GMT
Server: Apache/2.0.61 (Unix)
Access-Control-Allow-Origin: http://api.bob.com
Access-Control-Allow-Methods: GET, POST, PUT
Access-Control-Allow-Headers: X-Custom-Header
Content-Type: text/html; charset=utf-8
Content-Encoding: gzip
Content-Length: 0
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
Content-Type: text/plain

这里面关键是的Access-Control-Allow-Origin字段,同3.1
(1). Access-Control-Allow-Methods
该字段必需,表明服务器支持的所有跨域请求的方法,而不单是浏览器请求的那个方法,这是为了避免多次"预检"请求。
(2). Access-Control-Allow-Headers
如果预检请求中有这个字段,则响应中这个字段是必须的,表明服务器支持的所有头信息字段,不限于浏览器在"预检"中请求的字段。
(3). Access-Control-Allow-Credentials
3.2
(4). Access-Control-Max-Age
可选,用来指定本次预检请求的有效期,单位为秒,在此期间,不用发出另一条预检请求。

如果预检请求被否定,会返回一个正常的HTTP回应,但是没有任何CORS相关的头信息字段,浏览器就知道预检请求被否定了。

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


5.完整请求流程

在这里插入图片描述
http://www.ruanyifeng.com/blog/2016/04/cors.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值