CORS
cross-origin resource sharing 跨域资源共享,允许web应用程序进行跨域访问控制,从而使跨域数据安全传输。
该方案不支持IE10以下,更多的跨域方案可参考 跨域方式。
什么情况下需要CORS?
跨域资源共享标准(cross-origin sharing standard)允许在以下场景中使用跨域HTTP请求:
- 由XMLHttpRequest或Fetch发起的跨域请求
- Web 字体 (CSS 中通过
@font-face
使用跨域字体资源) - WebGL 贴图
- 使用
[drawImage](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/drawImage)
将 Images/video 画面绘制到 canvas - 样式表(使用 CSSOM)
功能概述
跨域资源共享标准新增了一组HTTP头部字段,允许服务器声明哪些源站通过浏览器有权限访问哪些资源。另外,对那些可能对服务器产生副作用的HTTP请求方法,浏览器必须先使用OPTIONS方法发起一个预检请求,从而获知服务器是否允许该跨域请求。服务器允许后才发起实际的HTTP请求。预检请求的返回中,服务器也可以通知客户端是否需要携带身份凭证(包括Cookies和HTTP认证相关数据)。
两种请求
浏览器将CORS请求分成两类:简单请求(simple request)和非简单请求(not-so-simple request)。简单请求不会触发CORS预检请求。
简单请求
需同时满足以下条件:
- 请求方法:GET、HEAD、POST
- 人为设置了以下HTTP的头部信息之外的其他头部字段:
Accept
Accept-Language
[Content-Language](https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/Content-Language)
Content-Type
:只限于三个值application/x-www-form-urlencoded、multipart/form-data、text/plain
- 请求中的任意
XMLHttpRequestUpload
对象均没有注册任何事件监听器;XMLHttpRequestUpload
对象可以使用XMLHttpRequest.upload
属性访问。 - 请求中没有使用
ReadableStream
对象。
对于简单请求,浏览器直接发出CORS请求。具体来说,就是在头信息之中,增加一个Origin
字段。响应中携带了响应首部字段 [Access-Control-Allow-Origin](https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/Access-Control-Allow-Origin)
。使用 Origin
和 Access-Control-Allow-Origin
就能完成最简单的访问控制。如果Origin
指定的源,不在许可范围内,服务器会返回一个正常的HTTP回应。浏览器发现,这个回应的头信息没有包含Access-Control-Allow-Origin
字段(详见下文),就知道出错了,从而抛出一个错误,被XMLHttpRequest
的onerror
回调函数捕获。注意,这种错误无法通过状态码识别,因为HTTP回应的状态码有可能是200。
非简单请求
不满足简单请求的条件即为非简单请求,需要先发送OPTIONS预检请求,预检请求完成后才发送实际的请求,就跟简单请求一样,会有一个Origin头部,服务器相响应中会有Access-Contrl-Allow-Origin头部字段。
HTTP请求首部字段
Origin(简单、非简单)
表明预检请求或实际请求的源站。它不包含任何路径信息,只是服务器名称。不管是否为跨域请求,ORIGIN 字段总是被发送。
Origin: <origin>
Access-Control-Request-Method(非简单)
用于预检请求,将实际请求所用的HTTP方法告诉服务器。
Access-Control-Request-Method: <method>
Access-Control-Request-Headers(非简单)
用于预检请求,将实际请求所携带的首部字段告诉服务器。
Access-Control-Request-Headers: <field-name>[, <field-name>]*
HTTP响应首部字段
Access-Control-Allow-Origin(简单、非简单)
Access-Control-Allow-Origin: <origin> | *
如果服务端指定了具体的域名而非“*”,那么响应首部中的 Vary 字段的值必须包含 Origin。这将告诉客户端:服务器对不同的源站返回不同的内容。
Access-Control-Allow-Methods(非简单)
Access-Control-Allow-Methods: <method>[, <method>]*
用于预检请求的响应。指明实际请求所允许的HTTP方法。
Access-Control-Allow-Headers(非简单)
Access-Control-Allow-Headers: <field-name>[, <field-name>]*
用于预检请求的响应。指明实际请求所允许携带的首部字段。
Access-Control-Allow-Credentials(简单、非简单)
它的值是一个布尔值,表示是否允许发送Cookie。默认情况下,Cookie不包括在CORS请求之中。
如果此头部字段在预检请求的响应中时,如果为true则表示实际请求的cookie可以包含在请求中,一起发送给服务器。
Access-Control-Allow-Credentials: true
如果要把Cookie发送到服务器,服务器需指定
Access-Control-Allow-Credentials:true
并且请求中需打withCredentials
var xhr = new XMLHttpRequest();
xhr.withCredentials = true;
简单GET请求不会被预检,如果对此类请求的withCredentials设置为true,而且响应中不包含该字段,这个响应 将被忽略,并且浏览器也不会将响应的内容返回给网页。
但是,如果省略withCredentials
设置,有的浏览器还是会一起发送Cookie。这时,可以显式关闭withCredentials
。
xhr.withCredentials = false;
需要注意的是,如果要发送Cookie,Access-Control-Allow-Origin
就不能设为星号,必须指定明确的、与请求网页一致的域名。同时,Cookie依然遵循同源政策,只有用服务器域名设置的Cookie才会上传,其他域名的Cookie并不会上传,且(跨源)原网页代码中的document.cookie
也无法读取服务器域名下的Cookie。
Access-Control-Expose-Headers
跨域访问时,XMLHttpRequest对象的getResponseHeader()方法只能拿到一些最基本的响应头,Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma,如果要访问其他头,则需要服务器设置本响应头。
此头部字段让服务器把允许浏览器访问的头放入白名单。
Access-Control-Expose-Headers: X-My-Custom-Header, X-Another-Custom-Header
这样浏览器就能够通过getResponseHeader访问X-My-Custom-Header
和 X-Another-Custom-Header
响应头了。
Access-Control-Max-Age(非简单)
Access-Control-Max-Age: <delta-seconds>
指定预检请求的结果能够被缓存多久。delta-seconds
参数表示preflight请求的结果在多少秒内有效,在此期间,浏览器不会发送另一条预检请求。
参考
https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Access_control_CORS
http://www.ruanyifeng.com/blog/2016/04/cors.html