一、CORS简介
CORS是一个W3C的标准,全称“跨域资源共享”(Cross-origin resource sharing)。
CORS允许浏览器向跨源服务器发出XMLHttpRequest请求,从而克服了AJAX只能同源请求的限制。CORS需要浏览器和服务器的共同支持。目前所有浏览器都支持此功能,IE浏览器版本不得低于IE10。
CORS的整个通信过程都是浏览器自动完成的,不需要用户参与。对于开发者来说,CORS通信与AJAX同源通信没有差别,代码完全一样。浏览器一旦发现AJAX请求跨源,就会自动添加一些附加的头信息,有时甚至会多出一次附加请求,但是用户不会有感觉,因为过程由浏览器自动完成。实现CORS通信的关键是服务器。只要服务器实现了CORS接口,就可以进行跨源通信。
二、CORS请求
浏览器将CORS请求分为两类,简单请求(simple request)和非简单请求(not-so-simple request),浏览器对两种请求的处理方式是不同的。满足以下条件为简单请求,否则为非简单请求。
简单请求成立条件:
- 请求方法为以下一种
- HEAD
- GET
- POST
- HTTP请求头不超出以下字段
- Accept
- Accept-Language
- Content-Language
- Last-Event-ID
- Content-Type
限定值:application/x-www-form-urlencoded、multipart/form-data、text/plain
三、简单请求
对于简单请求浏览器的处理方式是在请求头添加Origin字段并发送CORS请求。
以下为CORS请求的例子的RequestHeads:
Accept: */*
Origin: http://127.0.0.1:8082
Referer: http://127.0.0.1:8082/h5/corsRequest.html
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36
请求头中Origin字段用来说明本次请求来自的源(协议://域名:端口)。服务器根据这个值来决定是否同意这个请求。
-
不同意请求
如果指定了Origin请求源,不在服务端的许可范围内,服务器会返回一个正常的HTTP响应。浏览器检查响应头没有Access-Control-Allow-Origin字段,即表示服务端不同意此次请求,浏览器抛出异常被XMLHttpRequest的onerror函数捕获。此错误无法通过响应状态码进行判断,尽管服务端不同意请求,请求仍正常响应。 -
同意请求
如果指定的Origin请求源在服务端的许可范围内,请求响应头会附加几个字段,字段如下:
Access-Control-Allow-Origin: http://127.0.0.1:8082
Access-Control-Allow-Credentials: true
Access-Control-Expose-Headers: Sivan
Content-Type: text/html; charset=utf-8
以上响应头字段中有三个是有关于CORS请求的,都以Access-Control-开头。
- Access-Control-Allow-Origin
此字段是必须的。值为请求头的Origin或*号(*号表示接受任意源的CORS请求)。 - Access-Control-Allow-Credentials
此字段可选。值为布尔值,表示是否允许发送Cookie。默认情况下CORS请求不包含Cookie。设置为true即明确表示服务端允许Cookie包含在CORS请求中,共同发送给服务端。此字段的值固定为true,如果不需要浏览器发送Cookie,删除字段即可。
CORS请求默认不发送Cookie和Http认证信息。如果要把这些信息共同发送到服务端,必须要服务端同意(指定Access-Control-Allow-Credentials:true请求头字段)和浏览器同意(在AJAX请求指定withCredentials:true属性)。如果省略withCredentials属性,部分浏览器还是会发送Cookie,可以显式关闭withCredentials(withCredentials:false)。
注意:如果要发送cookie则Access-Control-Allow-Origin请求头字段不能为*号,必须指明与CORS请求源一致的域名。同时Cookie依然遵守同源策略,只有以服务端域名设置的才会发送,且(跨源)原网页代码中的document.cookie也无法读取服务端域名下的Cookie。
- Access-Control-Expose-Headers
此字段可选。CORS请求时,XMLHttpRequest对象的getResponseHeader()函数只能拿到六个基本字段:Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma。获取其他响应头字段需要在Access-Control-Expose-Headers指定。以上例子表示服务端指定Siven响应头字段可获取(Access-Control-Expose-Headers: Sivan)。
四、非简单请求
非简单请求即对服务端有特殊要求的请求,例如请求方法是PUT或DELETE之类的,或Content-Type是application/json等。
非简单请求会在正式通信前发送预检(preflight)请求,询问服务端当前网页所在的域名是否在CORS许可范围内,以及请求头和Content-Type是否在允许范围内。在得到服务端的肯定答复后会发送正式的XMLHttpRequest请求,反之报错。
-
预检请求(preflight)
预检请求(preflight)
预检请求使用的请求方法是OPTIONS,表示这个请求是用于询问的。以下列出请求头里的相关字段:
- Origin
字段必选。预检请求关键字段,表明预检请求的源。- Access-Control-Request-Headers
字段必选。用于表明浏览器端的CORS请求时用到的HTTP方法。- Access-Control-Request-Method
字段非必选。字段为逗号分隔字符串。用于表明浏览器端的CORS请求会使用到的额外请求头字段。
- 预检请求响应
服务端收到预检请求后,检查Origin、Access-Control-Request-Headers、Access-Control-Request-Method是否在跨源请求的范围内,并作出响应。
-
预检请求不通过
如果服务端否认了预检请求,作出正常的HTTP响应,但是响应头没有任何相关CORS的字段,响应状态码一般为403无权访问。此时浏览器通过判断得知服务端否定了预检请求,触发错误并未XMLHttpRequest的onerror回调函数捕获,控制台打印出异常。
预检请求不通过
-
预检请求通过
如果服务端确定了预检请求,作出正常回应,响应投中包含CORS相关字段,响应码一般为200。浏览器监听到服务端确定了预检请求则自动发出正式的CORS请求。
预检请求响应
响应头字段如下:
Access-Control-Allow-Origin
字段必选。预检请求响应关键字段。表示服务端允许CORS请求的源,如果设置为*则表示任意源皆可发送CORS请求(跨源请求)。
Access-Control-Allow-Credentials
字段非必选(布尔值)。与简单请求的含义相同,表示服务端同意接口Cookie。
Access-Control-Allow-Headers
字段可选。如果浏览器请求头包含Access-Control-Request-Headers字段则响应头的Access-Control-Allow-Headers字段为必选。字段为逗号分隔字符串,用于表明服务端所支持的CORS请求的请求头字段。
Access-Control-Allow-Methods
字段必选。值为逗号分隔字符串。表示服务端接收CORS请求所支持的请求方法。
Access-Control-Expose-Headers
字段可选。值为逗号分隔字符串。与简单请求的含义一致。表示服务端所支持的跨源请求自定义请求头字段。
Access-Control-Max-Age
字段可选。表示本次预检请求的有效期,即在本次预检有效期间(缓存预检请求)将不再发送预检请求。单位为秒。
- 正常请求与正常回应
在服务端通过预检请求之后,如果设置了预检请求有效期,则浏览器发送非简单请求与简单请求的方式一致(直至预检请求失效),请求头附加Origin字段表明请求源,响应头也会有Access-Control-Allow-Origin字段。
网友评论