对于跨域,随着w3c的CORS的出现,相比较于有些年头的jsonp,CORS以其简单安全,支持post的优点愈来愈收到你们的欢迎。具体如何CORS的原理和实现,直接推荐阮老师的文章,十分详细。本文主要关注CORS实现过程当中的几个疑惑点。html
浏览器将CORS请求分红两类:简单请求(simple request)和非简单请求(not-so-simple request)。前端
同时知足一下条件的便是简单请求:java
显然,不一样时知足则为非简单请求(能够认为是复杂请求)。二者的差异在于复杂请求在与服务端交互时多了一次options的预检请求,毕竟复杂请求通常就是HTTP请求头信息超出限制或者method为put、delete等操做行为,处于安全考虑,须要服务端先行验证来决定是否给予相关权限。jquery
以下所示(示例来自阮老师文章):ios
var url = 'http://api.alice.com/cors';
var xhr = new XMLHttpRequest();
// PUT method为复杂请求,要预检
xhr.open('PUT', url, true);
xhr.setRequestHeader('X-Custom-Header', 'value');
xhr.send();
复制代码
非简单请求,浏览器自动发送otpios的预检请求,请求头以下:nginx
OPTIONS /cors HTTP/1.1
// 请求源
Origin: http://api.bob.com
// 必须字段,指明正式cors请求将会使用那些method
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...
复制代码
对于预检信息,服务端通常作了以下操做:ajax
一、检查origin、Access-Control-Request-Method和Access-Control-Request-Headers等字段,确认是否容许跨域,若是容许跨域做出回应:shell
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
// 容许额外header
Access-Control-Allow-Headers: X-Custom-Header
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相关的信息。浏览器则会认为服务器不容许跨域,触发错误。json
// 常见的跨域错误
XMLHttpRequest cannot load http://api.alice.com.
Origin http://api.bob.com is not allowed by Access-Control-Allow-Origin.
复制代码
到这里一个流程结束,不过咱们要关注的是options 预检请求以后 code 返回的问题axios
常规预检的就是对于options的请求直接返回code 200的响应,表示校验经过。 可是前两天发现有的返回为code204。二者之间的差异具体在哪呢。
一、针对特定接口支持CORS时,在代码里加判断对于options返回200
// 随便找了段java代码
if (req.getMethod().equals("OPTIONS")) {
res.setStatus(200);
}
复制代码
二、若是整个域名都支持CORS,能够再nginx侧直接配置,此时常见的是返回204.
if ($request_method = 'OPTIONS') {
add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Methods GET,POST,PUT,DELETE,OPTIONS;
#****省略...
return 204;
}
复制代码
二者之间的差异,首先能够参考下204和200 对应的含义(下面内容摘自MDN)。 200 请求成功,成功的具体含义依据http method 的不一样而有所差异。:
204 服务器成功处理了请求,但不须要返回任何实体内容,而且但愿返回更新了的元信息。 客户端是浏览器的haul,用户浏览器应保留发送了该请求的页面,而不产生任何文档视图上的变化。因为204响应被禁止包含任何消息体,所以它始终以消息头后的第一个空行结尾。
简单总结,204返回表示请求成功,而且无消息体,优点在于节省网络请求。
贴切的来讲,应该像其余options请求同样为预检optiosn请求返回相同的code状态码,相关规范不要求或者推荐其余内容。
fecth请求 例如对于Fetch 规范 要求CORS协议的status能够为200-209里面的任意值。
If a CORS check for request and response returns success
and response’s status is an ok status,
run these substeps.
复制代码
若是response为一个okstatus就能够继续执行
An ok status is any status in the range 200 to 299, inclusive.
复制代码
并不要求具体哪个值。 因此从fetch来看,二者都可选择。
HTTP 1.1 对于http/1.1 规范来讲,有一章节专门定义了各类响应code。对于2开头的2-XXcode,分别描述以下:
204 服务器成功处理了请求,但不须要返回任何实体内容,而且但愿返回更新了的元信息。 客户端是浏览器的话,用户浏览器应保留发送了该请求的页面,而不产生任何文档视图上的变化。因为204响应被禁止包含任何消息体,所以它始终以消息头后的第一个空行结尾。
首先二者均可以使用,对于200,从定义而言更符合场景和定义。可是204无消息体,优点在于节省网络请求。
至于用哪一个,你们自行作下判断。
做为常见的场景,cookie通常会存放一些,鉴权会话等信息。对于CORS跨域,默认的是不包含cookie的。
A cross-origin request by default does not bring any credentials (cookies or HTTP authentication)
复制代码
若是要操做cookie须要分别从服务端和客户端两个场景来看。
request若是要携带cookie,须要特定参数指明。可能看到过这个参数为credentials或者withCredentials,何时用二者呢。主要跟请求的实现有关:
Fetch 使用credentials 直接使用原生Fetch的话,须要设置credentials。
credentials 是Request接口的只读属性,用于表示用户代理是否应该在跨域请求的状况下从其余域发送cookies。这与XHR的withCredentials 标志类似,不一样的是有三个可选值(后者是两个):
CORS跨域的时候,只须要以下设置:
fetch('http://another.com', {
credentials: "include"
});
复制代码
var xhr = new XMLHttpRequest();
xhr.open('GET', 'http://example.com/', true);
xhr.withCredentials = true;
xhr.send(null);
复制代码
适用框架:jquery的ajax,axios等。
当客户端设置了容许携带cookie以后,并不能完成该操做,毕竟是跨域,服务端也须要作响应设置,不然浏览器拿不到正确响应。
Access-Control-Allow-Credentials:true
复制代码
看MDN 的解释:
The Access-Control-Allow-Credentials response header tells browsers whether to expose the response to frontend JavaScript code when the request's credentials mode (Request.credentials) is "include".
复制代码
当 credentials为include的时候,通知浏览器是否将响应暴露给前端jscode,若是为false,js不能读取响应天然请求报错。 只有Access-Control-Allow-Credentials为true时,才会将响应暴露给客户端。 看成为预检请求响应头时,代表该实际请求(即后面的真正请求)是否可使用credentials。
不过对于简单请求,由于没有预检,若是服务端没有正确响应,浏览器会忽略该属性,并不会直接报错。
须要与XMLHttpRequest.withCredentials属性或者Fetch 的credentials 配合使用。
若是要发送Cookie,Access-Control-Allow-Origin就不能设为星号,必须指定明确的、与请求网页一致的域名。
同时,Cookie依然遵循同源政策,只有用服务器域名设置的Cookie才会上传,其余域名的Cookie并不会上传。
且(跨源)原网页代码中的document.cookie也没法读取服务器域名下的Cookie。
毕竟cookie是有path来保证封闭性的,若是能够随便读取无论从安全仍是性能上都是一种隐患。
对于多域名跨域,方法比较多。
容许任意域名跨域,显然支持多域名。不过从安全性和cookie的使用的角度来看并不推荐。
这种实现方式比较多,原理就是声明容许的多域名配置,能够是数组或者是正则,根据当前请求的域名,来判断是否在适用返回内,在的话则设置Access-Control-Allow-Origin为当前域名。
具体实现这里就不写了。
www.ruanyifeng.com/blog/2016/0…
fetch.spec.whatwg.org/#cors-proto…
www.yunweipai.com/archives/93… 以上是在工做中偶然发现的几点疑惑,解决以后深究了下具体原理。但愿能对其余同窗有所帮助,抛砖引玉,一块儿努力。