浏览器跨域请求的机制:CORS

系统 浏览器
CORS 真正的形态是非简单请求,它不会立即发送真正的请求,而是额外发送 OPTION 方法的预检请求,让服务端来决定是否允许即将发送的请求。

大家好,我是前端西瓜哥。

因为同源策略(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 的话。



责任编辑:姜华 来源: 今日头条
相关推荐

2020-08-31 19:20:33

浏览器CORS跨域

2023-12-20 14:42:59

2015-04-24 10:37:40

Web安全浏览器跨域访问

2021-06-10 18:11:02

Cors跨域Web开发Cors

2023-12-12 09:45:16

前端浏览器

2024-08-23 09:00:18

开发跨域请求

2019-04-10 10:32:16

CORSNginx反向代理

2024-11-21 10:38:10

2021-06-15 07:32:59

Cookie和Sess实现跨域

2014-08-19 10:36:02

AngularCORS

2022-03-21 07:35:34

处理方式跨域

2017-04-26 14:15:35

浏览器缓存机制

2021-06-25 09:04:39

Cors跨域JSONP vs CO

2017-05-15 13:40:20

浏览器http缓存机制

2010-09-14 14:18:09

CSS跨浏览器开发

2020-12-20 18:00:04

跨域请求开发CORS

2010-05-31 10:11:02

2020-11-13 11:15:17

数据加密攻击模型浏览器密码

2021-07-22 09:55:28

浏览器前端缓存

2021-06-06 13:05:15

前端跨域CORS
点赞
收藏

51CTO技术栈公众号