CORS 是什么?
跨域资源共享
是一种机制,它使用额外的头
来告诉浏览器 让运行在一个 origin (domain) 上的Web应用被准许访问
来自不同源服务器
上的指定的资源。当一个资源从与该资源本身所在的服务器不同的域、协议或端口请求一个资源时,资源会发起一个跨域 HTTP 请求。
什么情况下需要使用CORS?
- XMLHttpRequest 和 fetch 请求
- WebGL
- Web字体
- 使用 drawImg 将 image/video 绘制到 canvas 上
- 样式表
CORS 请求的规范
当浏览器检测到某个请求跨域之后, 就会根据请求的类型发送CORS规定的网络请求。具体是指,简单请求和非简单请求具有差异性。
对于简单请求, 只要服务端允许跨域, 具体是设置了Access-Control-Allow-Origin
字段为*
。这样会带来安全问题, 后面会继续讨论。
对于非简单请求, 一般都会对服务器的数据进行一些操作, 所以会首先发起预检请求,询问服务器是否允许这次的访问。如果允许, 在发起正常的网络请求。
预检请求
使用HTTP1.1的OPTIONS方法发起预检请求, 这个请求用于向服务端获取更多的信息, 但是不会对服务器的资源造成影响。
预检请求会携带一下两个字段:
Access-Control-Request-Method: POST
Access-Control-Request-Headers: X-PINGOTHER, Content-Type
首部字段 Access-Control-Request-Method
告知服务器,实际请求将使用 POST
方法。首部字段Access-Control-Request-Headers
告知服务器,实际请求将携带两个自定义请求首部字段:X-PINGOTHER
与 Content-Type
。服务器据此决定,该实际请求是否被允许。
预检请求的响应
浏览器发起预检请求之后,服务器发送回响应。响应携带如下重要字段:
Access-Control-Allow-Origin: http://foo.example
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Headers: X-PINGOTHER, Content-Type
Access-Control-Max-Age: 86400
Access-Control-Allow-Methods
: 表示允许请求的方法有POST, GET, OPTIONS
Access-Control-Allow-Headers
: 请求允许的头部信息类型为: X-PINGOTHER, Content-Type
, 逗号分割列表。
Access-Control-Max-Age
: 此次预检请求的有效期, 86400秒, 在有效期内, 发送相同请求不需要再发送预检请求,如果超过这个时间, 对于同一个请求, 需要再次发起预检请求。
HTTP 首部响应字段
Access-Control-Allow-Origin: <origin>
: origin
参数的值指定了允许访问该资源的外域 URI。对于不需要携带身份凭证的请求,服务器可以指定该字段的值为通配符(*)
,表示允许来自所有域的请求。
Access-Control-Max-Age: <delta-seconds>
: 指定一次预检请求的有效期。有效期内不会再次发送预检请求。
Access-Control-Allow-Methods: <method>[, <method>]*
: 允许请求的方法(get, post ...)
Access-Control-Expose-Headers: X-My-Custom-Header, X-Another-Custom-Header
: 服务器把允许浏览器访问的头放入白名单.
在跨域访问时,XMLHttpRequest对象的getResponseHeader()方法只能拿到一些最基本的响应头,Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma,如果要访问其他头,则需要服务器设置本响应头。
Access-Control-Allow-Headers: <field-name>[, <field-name>]*
: 预检响应告知浏览器允许的实际请求头类型。
CORS withCredentials 属性
CORS请求默认不发送Cookie和HTTP认证信息。如果要把Cookie发到服务器,一方面要服务器同意, 另一方面,开发者必须在AJAX请求中打开withCredentials属性。
server:
Access-Control-Allow-Credentials: true
developer:
var xhr = new XMLHttpRequest();
xhr.withCredentials = true;
二者缺一不可。
需要注意的是,如果要发送Cookie,Access-Control-Allow-Origin就不能设为星号,必须指定明确的、与请求网页一致的域名。同时,Cookie依然遵循同源政策,只有用服务器域名设置的Cookie才会上传,其他域名的Cookie并不会上传,且(跨源)原网页代码中的document.cookie也无法读取服务器域名下的Cookie。
HTTP 首部请求字段
Origin: <origin>
: 表示请求的源, origin 参数的值为源站 URI。它不包含任何路径信息,只是服务器名称。不管是否为跨域请求,ORIGIN 字段总是被发送。
Access-Control-Request-Method: <method>
: 将实际请求所使用的 HTTP 方法告诉服务器。
Access-Control-Request-Headers: <field-name>[, <field-name>]*
: 将实际请求所携带的首部字段告诉服务器。
以上, 来自MDN。
浏览器阻止跨域的两种方式
- 请求正常发起, 但是返回的数据被浏览器拦截
- 请求发起的时候被浏览器拦截
CORS 的隐患
CSRF(Cross-site request forgery)跨站请求伪造
利用可跨域这一特性。