跨域哪些事——4. 使用CROS实现跨域

社区前端

如果还不了解跨域和同源策略的同学,可以去看一下第一篇,链接如下:跨域哪些事——1.如果没有跨域,世界会是怎样? - 文章 - 开发者社区 - 火山引擎 (volcengine.com)

1.简介

首先我们看一下什么是CROS,跨域资源共享(英语:Cross-origin resource sharing,缩写:CORS),用于让网页的受限资源能够被其他域名的页面访问的一种机制。

由于同源策略跨域的限制,我们Ajax请求往往会被禁止,但是如果我们有一些可以确定是安全的跨域请求呢,这个时候我们该怎么办呢?我们知道有上一篇说的JSONP方式,想了解的可以看上一篇:跨域哪些事——3. 使用JSONP实现跨域 - 文章 - 开发者社区 - 火山引擎 (volcengine.com)。但是JSONP只支持get请求,很多时候特别是开发阶段,前端会起前端的项目,和后端的请求当然是跨域的,这时候JSONP就不行了,后端还是已POST请求为主的。于是我们CROS方案就出现了。

Tellme Networks 的马特·奥什里(Matt Oshry)、布拉德·波特(Brad Porter)与麦克·波德尔(Michael Bodell)于 2004 年 3 月提案将跨来源支持加入 VoiceXML 2.1以支持 VoiceXML 浏览器的跨来源资料请求。W3C 认为这不应该限制在 VoiceXML 而是一般的机制,因此将提案移到另一份实现备忘录。几个主要的浏览器厂商透过 W3C 的 Web 应用程序工作小组正式的将该备忘录改写为 W3C 工作草案并以推动成为 W3C 推荐标准为目标。

2.原理

说到原理,如果让人理解其实很简单,就是W3C定义了一大堆规范,来对跨域做校验,让想跨域的浏览器和服务器都进行一定的校验,然后就可以完成跨域了,相当与在浏览器和服务器之间加了一个中间层。所以CROS跨域也只是浏览器和服务器之间的跨域,不能完成浏览器页面之间的跨域。

那到底是怎么校验的呢?分为2种,简单请求和非简单请求。以简单请求为例,凡是不同时满足下面面两个条件,就属于非简单请求。

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

HEAD

GET

POST

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

Accept

Accept-Language

Content-Language

Last-Event-ID

Content-Type:只限于三个值application/x-www-form-urlencoded、multipart/form-data、text/plain

比如我们常见的类型 application/json 现在看就不是简单请求。

2.1 简单请求

浏览器发现这次跨源AJAX请求是一般请求,就自动在头信息之中添加一个Origin字段。

#Origin字段用来说明本次请求来自哪个源(协议 + 域名 + 端口),服务器根据这个值,决定是否同意这次请求。


GET /cors HTTP/1.1

Origin: [http://www.explem.com] #判断是否存在CORS安全问题

Host: api.alice.com

Accept-Language: en-US

Connection: keep-alive

User-Agent: Mozilla/5.

而返回的响应会列出来现在的服务器允许哪些跨域的参数

响应

Access-Control-Allow-Origin: www.explem.com #请求时Origin字段的值或者是一个*(表示接受任意域名的请求)


Access-Control-Allow-Credentials: true #布尔值,表示是否允许发送Cookie(默认是否的)|浏览器不同则不同(有人说第一次请求都会发生),其次看服务器是否接收;

Access-Control-Expose-Headers: FooBar #CORS请求时,XMLHttpRequest对象的getResponseHeader()方法只能拿到6个基本字段,为了能拿到字段就要设置

Content-Type: text/html; charset=utf-8

然后如果发的请求在允许范围内,就可以正常通信了。

2.2 非简单请求

非简单请求的CORS请求,会在正式通信之前增加一次HTTP查询请求,

称为"预检"请求(preflight);如果浏览器否定了”预检”请求,会返回一个正常的HTTP回应,

预检请求


OPTIONS /cors HTTP/1.1 #"预检"请求用的请求方法是OPTIONS,表示这个请求是用来询问的

Origin: [http://www.explem.com](http://api.bob.com)

Access-Control-Request-Method: PUT #列出浏览器的CORS请求会用到哪些HTTP方法

Access-Control-Request-Headers: X-Custom-Header #逗号分隔的字符串,指定浏览器CORS请求会额外发送的头信息字段

Host: api.alice.com

Connection: keep-alive

User-Agent: Mozilla/5.0
...

预检请求的回应

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://www.explem.com] #关键点,该字段也可以设为星号,表示同意任意跨源请求。

Access-Control-Allow-Methods: GET, POST, PUT #关键点,它也是一个逗号分隔的字符串,表明服务器支持的所有头信息字段,不限于浏览器在"预检"中请求的字段。

Access-Control-Allow-Headers: X-Custom-Header #关键点,它也是一个逗号分隔的字符串,表明服务器支持的所有头信息字段,不限于浏览器在"预检"中请求的字段。

Access-Control-Max-Age: 1728000 #关键点,用来指定本次预检请求的有效期,单位为秒。上面结果中,有效期是20天(1728000秒)

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

下面是”预检”请求之后浏览器的正常CORS请求,下面头信息中Access-Control-Allow-Origin字段是每次回应都必定包含的。

浏览器的正常请求和回应


PUT /cors HTTP/1.1

Origin: [http://www.explem.com] #默认是浏览器自动添加的

Host: api.alice.com

X-Custom-Header: value

Accept-Language: en-US

Connection: keep-alive

User-Agent: Mozilla/5.0...

服务器正常的回应


Access-Control-Allow-Origin: [http://www.explem.com]
Content-Type: text/html; charset=utf-8

picture.image

3.总结

我们再看一下CROS这种方式。

首先他是当前前端和后端跨域的最佳方案。解决了JSONP不能发送POST请求的问题。

他的限制是需要新版本的浏览器支持(当然现在的浏览器都支持,只有一些老电脑不支持)。需要后端服务器添加对应的跨域规则。

0
0
0
0
评论
未登录
看完啦,登录分享一下感受吧~
暂无评论