浏览器跨域请求的机制:CORS
大家好,我是前端西瓜哥。因为同源策略(Cross-Origin Policy)的存在,浏览器在一个域名下发送另一个域名的 Ajax 请求时,返回的数据通常会被浏览器拦截,让开发者无法拿到返回结果。这里说 “通常”,是因为浏览器额外提供了一种可以正常使用 Ajax 请求非同源域名接口的机制,也就是我们接下来要说的 跨源资源共享(CORS, Cross-Origin Resource Sharing)
大家好,我是前端西瓜哥。
因为同源策略(Cross-Origin Policy)的存在,浏览器在一个域名下发送另一个域名的 Ajax 请求时,返回的数据通常会被浏览器拦截,让开发者无法拿到返回结果。
这里说 “通常”,是因为浏览器额外提供了一种可以正常使用 Ajax 请求非同源域名接口的机制,也就是我们接下来要说的 跨源资源共享(CORS, Cross-Origin Resource Sharing)。
CORS 会在上图中的第三步中发挥作用。
简单来说,就是请求 b.com 的 HTTP 响应头字段中的一些头字段符合一定规则,返回数据就不会被浏览器拦截,请求者能正常获得返回数据。
简单请求和非简单请求是浏览器发出跨域请求的 两种不同的请求方式,我们来了解一下。
简单请求
发送的请求符合下面的所有情况,就属于简单请求。
-
请求方法为其中一种:
GET
、POST
、HEAD
; -
除了浏览器自动设置的字段(比如
Connection
),仅能人为设置请求头字段Accept
、Accept-Language
、Content-Language
、Content-Type
这个集合内的值。 -
请求头字段
Content-Type
为其中一种:text/plain
、multipart/form-data
、application/x-www-form-urlencoded
;
(还有一些更多的琐碎的细节,比如脚本设置、特定浏览器相关的,这里不展开,具体可以查阅官方文档)
可能你会对这个规则的设计很感兴趣,其实它是为了 向后兼容,兼容一些之前没有 Ajax 时就可以发送的跨域请求,比如 form 元素能够产生的请求。
点击表单下的提交按钮,页面跳转然后发送请求。这种写法非常古老了,因为会自动跳转页面且不能拿到返回数据执行下一步的操作,基本开
<form action="https://b.com/api/v1/book/get" method="get">
<input type="text" name="name" value="clean code" />
<input type="submit" value="提交"/>
</form>
CORS 机制下,HTTP 响应头字段需要使用头字段 Access-Control-Allow-Origin
,将它的值设置为:
-
*
:任意域名都允许跨域请求。 -
<origin>
:具体的域名,这里不能提供多个域名,只能提供一个域名。这也是可以理解的,通过强制要求防止黑客得到服务端设置的白名单域名。我们不应该透露太多信息。服务端要实现跨域白名单功能,就需要根据请求头字段中
另外,如果请求中携带了身份信息,也就是 Cookie,不能使用 *
,而需要指定具体域名。
这样设置后,我们就能正常地拿到其他域名接口返回的数据了。
下面我们再看看非简单请求。
非简单请求
不符合简单请求规定条件的请求,就是 非简单请求。
对于简单请求,为了兼容,浏览器会直接将请求发出去。但非简单请求没有兼容的需要,所以浏览器给它加上了严格的复杂机制。
在发出真正的请求前,浏览器会先发一个 OPTION 请求来探探路,这个请求称为 预检请求(preflight)。
浏览器发送的请求,会额外带下以下请求头字段:
-
Access-Control-Request-Method
:该字段告知服务端接下来要发起真正跨域请求使用的 HTTP 方法。 -
Access-Control-Request-Headers
:该字段告知服务端,当前请求使用的不符合简单请求规则的头字段,比如使用 JSON 结构来作为数据的格式,预检请求就会带上上Access-Control-Request-Headers: content-type
下面是服务端的回合。
服务端拿到了足够多的请求方信息,然后就可以考虑是否允许跨域了。
服务端的响应头字段可以携带上以下字段:
-
Access-Control-Allow-Origin
:用法同上一节的简单请求说明。 -
Access-Control-Allow-Methods
:可以发送的请求方法。如POST, GET, OPTIONS
。 -
Access-Control-Allow-Headers
:可以使用的头字段,如X-PINGOTHER, Content-Type
-
Access-Control-Max-Age: 86400
:缓存的有效时长,单位为秒,设置后,在这段时间内都可以免除预检请求的发送。浏览器自身预设了一个最大缓存时间,防止返回的缓存时间过长(比如好几年)导致的安全问题。
浏览器拿到这些字段后,就会进行对比,如果不符合规则,那么真正的跨域请求将不会发送。如果符合,接下来就会发送真正的跨域请求。
需要注意的是,真正的请求和简单请求一样,需要提供 Access-Control-Allow-Origin
头字段,否则依旧拿不到返回数据。
结尾
CORS 真正的形态是非简单请求,它不会立即发送真正的请求,而是额外发送 OPTION 方法的预检请求,让服务端来决定是否允许即将发送的请求。
简单请求是向后兼容的妥协产物,它其实直接向目标发起了真正请求,只是浏览器可能会将返回的数据拦截掉,如果响应头没有设置正确的 Access-Control-Allow-Origin
的话。
我是前端西瓜哥,欢迎关注我。
前端西瓜哥
坚持日更原创,分享前端知识。
104篇原创内容
公众号
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)