CORS, Cross-Origin Resources Sharing, 跨域资源共享.
允许浏览器向跨域服务器请求资源, 从而让AJAX克服同源策略.
CORS需要浏览器和服务器同时支持.
随着W3C标准的推动, 目前所有浏览器都支持CORS.
CORS请求与同源的AJAX请求代码没有差别.
但当浏览器发现AJAX请求跨域资源时, 会自动添加一些附加的头信息, 有时还会多出一次附加的请求.
整个过程有浏览器自动完成, 不需要用户参与.
1. 2种请求模式
1.1. 简单模式
简单模式, 浏览器直接向服务器发起简单类型跨域请求, 浏览器与服务器之间请求只进行一次.
需要满足以下2个条件:
- 使用下列方法之一:
- GET
- HEAD
- POST
- 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 | GET /cors HTTP/1.1 |
Origin字段说明了本次请求来自哪个域(协议, 域名及端口).
服务器根据该字段, 决定是否同一这是请求.
如果Origin指定的域不在许可返回内, 服务器就会返回一个正常的HTTP响应, 且响应头中不包含Access-Control-Allow-Origin字段.
浏览器发现没有Access-Control-Allow-Origin字段, 就知道出错了.
跨域请求失败无法通过状态码来识别, 因为状态码有可能是200.
如果Origin指定的域在许可返回内, 那服务器返回的响应, 会多出几个头信息字段.
1 | Access-Control-Allow-Origin: http://api.bob.com |
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, 则需要:
- 客户端设置AJAX请求打开
withCredentials属性:
1 | var xhr = new XMLHttpRequest();bkk |
- 服务器明确同意接收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 | OPTIONS /cors HTTP/1.1 |
预检请求使用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 | XMLHttpRequest cannot load http://api.alice.com. |
如果确认允许跨域, 给出回应.
1 | HTTP/1.1 200 OK |
确认通过预检请求之后, 才会发起正常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天内不用发起另一条预检.