1. 简介
CORS(Cross Origin Resource Shared)是一种对服务端请求资源时的权限限制,出于安全原因考虑,浏览器在限制在通过脚本获取资源的时候只能获取同源的数据资源,如果要获取非同源的数据资源,则需要请求时增加CORS头信息。
非同源的数据资源是指需要访问的数据资源和当前的站点具有不同域名(domain),端口(port),协议(protocol)等。
2. 产生跨域条件
并不是所有的请求都会需要产生跨域。
首先,跨域是浏览器为了安全的一种限制,因此部分浏览器是可以关闭跨域安全限制的。
其次,跨域是利用脚本请求数据资源时才会带来的限制,于是对于非脚本请求,例如通过浏览器直接访问一个数据资源是不会产生跨域的。
总的来说,通常以下几种情况会产生跨域:
- 使用
XMLHttpRequest
或者Fetch API
请求资源- css中使用
@font-face
获取web字体资源- WebGL texture
- img或者video使用了Canvas的drawImage
3. 跨域请求和响应
通过跨域请求时所携带的请求信息不同,服务端需要有不同的响应,这里所说携带的请求信息主要指请求的头信息中携带的信息
3.1 简单请求
简单请求是指请求的时候不会优先发送OPTIONS
请求的跨域请求
3.1.1 条件
简单请求有5个限制条件
- 请求的
Method
只能是GET
,POST
,HEAD
请求- 请求的
Headers
只能包含Accept
,Accept-Language
,Content-Language
,Content-Type
,Last-Event-ID
,DPR
,Save-Data
,Viewport-Width
,Width
- 请求的
Headers
中的Content-Type
只能是application/x-www-form-urlencoded
,multipart/form-data
,text/plain
XMLHttpRequest
中没有使用xhr.upload
对象- 没有使用
ReadableStream
对象
3.1.2 过程
对于简单请求,浏览器会在请求的时候头信息中添加Origin
,等待服务端返回的ResponseHeaders
中携带的Access-Control-Allow-Origin
信息,来判断是否可以访问资源
参考MDN中的请求过程图
3.2 Preflight请求
Preflight
请求在跨域请求的时候会优先发送OPTIONS
请求到服务器,根据响应来当前的请求是否是安全的,是否可以继续请求资源数据
3.2.1 条件
所有非简单请求都会发送Preflight
请求
3.2.2 过程
对于Preflight
请求,首先发送OPTIONS
请求,并在头信息中包含Origin
,Access-Control-Request-Header
,Access-Control-Request-Method
信息,服务端会根据设置在响应头信息中增加Access-Control-Allow-Origin
,Access-Control-Allow-Headers
,Access-Control-Allow-Methods
,来告知浏览器当前请求是否支持,如果不支持,则停止请求;如果支持,则浏览器会再次发起相应Method
的请求,并携带Origin
信息,服务端会再次在响应头中返回Access-Control-Allow-Origin
信息,并返回数据信息。
同样在MDN上有一个很清楚的请求过程的图解
3.3 withCredentials请求
3.3.1 作用
特别的通过XmlHttpRequest
或者Fetch API
进行跨域请求的时候,通常是不会携带Cookie
信息或者HttpAuthentication
信息的,但是如果不携带Cookie
信息,例如:如果使用了服务端Session
,因为不携带Cookie
信息,那么就没办法获取到对应的Session
。于是需要增加withCredentials
设置
3.3.2 过程
以XMLHttpRequest
为例,在请求的时候需要设置xhr.withCredentials=true
,之后在客户端发起请求的时候会传递Cookie
信息,服务端在接受到信息以后会在响应头返回Access-Control-Allow-WithCredentials
和Set-Cookie
信息,浏览器通过返回的信息判断请求是否有效
也可以参见MDN的请求过程图解
这里需要注意的是,如果服务端设置了
Access-Control-Allow-WithCredentials: true
,那么我们在服务端配置的Access-Control-Allow-Origin
不能使用*
的通配符
4. 总结
总的来说,跨域是要特定情况下才会产生的,最常见的就是使用AJAX
来请求数据资源的时候产生跨域;跨域请求会根据头信息不同,浏览器会判断是否优先发送OPTIONS
请求;对于需要传递Cookie
的请求,我们需要设置withCredentials