0%

CORS

CORS, Cross-Origin Resources Sharing, 跨域资源共享.
允许浏览器向跨域服务器请求资源, 从而让AJAX克服同源策略.
CORS需要浏览器和服务器同时支持.
随着W3C标准的推动, 目前所有浏览器都支持CORS.

CORS请求与同源的AJAX请求代码没有差别.
但当浏览器发现AJAX请求跨域资源时, 会自动添加一些附加的头信息, 有时还会多出一次附加的请求.
整个过程有浏览器自动完成, 不需要用户参与.

1. 2种请求模式

1.1. 简单模式

简单模式, 浏览器直接向服务器发起简单类型跨域请求, 浏览器与服务器之间请求只进行一次.

需要满足以下2个条件:

  1. 使用下列方法之一:
  • GET
  • HEAD
  • POST
  1. HTTP的头信息不超过以下几种:
  • Accept
  • Accept-Languagebkk
  • Content-Language
  • Last-Event-ID
  • Content-Type, 该字段值必须是application/x-www-form-urlencoded, multipart/form-data, 或text/plain.

不满足简单模式的条件, 则需要使用预检模式.

1.1.1. 简单模式流程

浏览器发现AJAX请求为跨域请求, 且是简单模式, 就会自动在头信息中添加Origin字段.

1
2
3
4
5
6
GET /cors HTTP/1.1
Origin: http://api.bob.com
Host: api.alice.com
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...

Origin字段说明了本次请求来自哪个域(协议, 域名及端口).
服务器根据该字段, 决定是否同一这是请求.

如果Origin指定的域不在许可返回内, 服务器就会返回一个正常的HTTP响应, 且响应头中不包含Access-Control-Allow-Origin字段.
浏览器发现没有Access-Control-Allow-Origin字段, 就知道出错了.
跨域请求失败无法通过状态码来识别, 因为状态码有可能是200.

如果Origin指定的域在许可返回内, 那服务器返回的响应, 会多出几个头信息字段.

1
2
3
4
Access-Control-Allow-Origin: http://api.bob.com
Access-Control-Allow-Credentials: true
Access-Control-Expose-Headers: FooBar
Content-Type: text/html; charset=utf-8

1.1.2. Access-Control-Allow-Origin

该字段在响应头中必定存在, 标识哪些域允许跨域.
可以是域列表;也可以是*, 表示允许任意域.

1.1.3. Access-Control-Allow-Credentials

CORS请求默认不发送Cookie.
如果服务器不需要Cookie, 则删除该自动即可.
Access-Control-Allow-Credentials若为true, 则Access-Control-Allow-Origin不能为*, 这也是出于安全考虑.

若需要发送Cookie, 则需要:

  1. 客户端设置AJAX请求打开withCredentials属性:
1
2
var xhr = new XMLHttpRequest();bkk
xhr.withCredentials = true;
  1. 服务器明确同意接收Cookie
1
Access-Control-Allow-Credentials: true

1.1.4. Access-Control-Expose-Headers

XMLHttpRequest对象的getResponseHeader()方法只能拿到6个基本字段:Cache-Control, Content-Language, Content-Type, Expires, Last-Modified, Pragma.
如果需要获取其他字段, 就必须在Access-Control-Expose-Headers中指定.

1.2. 预检模式 preflight

预检模式, 会在正式请求之前, 先发起一次预检请求.
先询问服务器, 当前域是否在许可范围内, 允许哪些请求方法以及需要哪些头信息字段.
只有得到服务器肯定答复后, 才会发起正式的请求, 否则报错.

1.2.1. 预检请求

当浏览器发现, 这是一个非简单模式的请求, 就会自动发起一个预检请求.

1
2
3
4
5
6
7
8
OPTIONS /cors HTTP/1.1
Origin: http://api.bob.com
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: X-Custom-Header
Host: api.alice.com
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...

预检请求使用OPTIONS方法, 表示用来询问的.
同样也有Origin关键字段.

1.2.2. Access-Control-Request-Method

该字段用来列出CORS请求会用到哪些HTTP方法, 如上面的PUT.

1.2.3. Access-Control-Request-Headers

该字段值是一个以逗号分隔的字符串, 用来指定浏览器CORS请求会额外发送的头信息字段, 如上面的X-Custom-Header.

1.2.4. 预检请求响应

服务器收到预检请求, 检查Origin, Access-Control-Request-Method, Access-Control-Request-Headers.

如果服务器否定了预检请求, 会返回一个正常的HTTP响应, 但其中不包括Access-Control-*.
浏览器会认定为不同意预检请求, 触发错误.
控制台会打印如下报错信息:

1
2
XMLHttpRequest cannot load http://api.alice.com.
Origin http://api.bob.com is not allowed by Access-Control-Allow-Origin.

如果确认允许跨域, 给出回应.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 01:15:39 GMT
Server: Apache/2.0.61 (Unix)
Access-Control-Allow-Origin: http://api.bob.com
Access-Control-Allow-Methods: GET, POST, PUT
Access-Control-Allow-Headers: X-Custom-Header
Access-Control-Allow-Credentials: true
Access-Control-Max-Age: 1728000
Content-Type: text/html; charset=utf-8
Content-Encoding: gzip
Content-Length: 0
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
Content-Type: text/plain

确认通过预检请求之后, 才会发起正常CORS请求, 流程与简单模式一样.

1.2.5. Access-Control-Allow-Methods

该字段必须, 为了避免多次预检, 返回的是服务器支持的所有跨域请求方法.

1.2.6. Access-Control-Allow-Headers

如果浏览器请求包括Access-Control-Allow-Headers, 则响应中也必须有该字段.
同样包含了所有服务器支持的头信息字段, 防止多次预检.

1.2.7. Access-Control-Max-Age

可选字段, 表示预检请求有效期, 单位为秒.
如上面的1728000, 则表示20天内不用发起另一条预检.

2. Resource