cors和koa-cors
brunoyang opened this issue · 2 comments
近期在工作中用到了cors,所以写下这篇分享,本文基于koa-cors 1.0.1
。
啥是cors呢
Cross-Origin Resource Sharing(cors),顾名思义,跨域资源共享,也就是一种实现跨域的手段,想要的解决的问题是跟使用jsonp一样的。要想知道跨域是为了什么,得先知道什么是跨域。跨域(cross-orgin)是因为有同源策略(same-origin policy)的存在。浏览器为了保证加载的脚本等资源都是可控安全的,就加了一个强制性的规定,非同域名下的资源不得加载。同域名是指两个域名之间,端口,协议,以及子域都应相同
域名 | 结果 | 原因 |
---|---|---|
http://www.alipay.com/dir/a.js | 成功 | 同一域名 |
http://news.alipay.com/a.js | 失败 | 非同一子域 |
https://www.alipay.com/a.js | 失败 | 协议不同 |
http://www.alipay.com:1234/a.js | 失败 | 端口不同 |
在chrome的network面板里可以看到,发给非同域的请求其实是发出去了的,只是浏览器在拿到对方服务器返回的时候数据时,看到这个响应并不是从当前域名返回的,就302了这个响应,同时报了个错。这样看来,跨域都是浏览器的锅,jsonp、iframe等手段都是为了绕过这一障碍才发明的。浏览器也不愿天天背这个锅,码农何苦为难码农,于是出现了cors这一手段,让浏览器天生支持跨域。
cors的实现非常简单,前端甚至都不需要做修改,只需要在对方服务端的响应头里加个字段Access-Control-Allow-Origin
就可以了。Access-Control-Allow-Origin
的值可以是*,也就是代表任何域名都可以来拿我的资源,也可以指定域名,只有指定的域名可以拉取资源。
cors还有些特性也需要详说的:
预请求(Preflighted)
预校验是指某些特殊情况下,需要在发送真正的ajax请求之前发送一个options
方法的请求,来『探测』一下我们的请求服务端接不接受,当然这一步是浏览器自动的,无需手动。『特殊情况』就是指下面的两种情况:
- 使用了
get
、head
和post
之外的方法,或post请求的Content-Type
是application/x-www-form-urlencoded
、multipart/form-data
、text/plain
以外的,如application/xml
等时; - ajax请求带有自定义请求头时。
在预请求的请求头里会额外再带上两个字段,分别是Access-Control-Request-Method
(必带),Access-Control-Request-Headers
(如果设置了自定义请求头),Access-Control-Request-Method
的值就是该ajax请求所用的方法。
带上cookie的ajax跨域请求(credentials)
可能有的同学会说,这根本不给力啊,如果我们做的是安全性比较高的服务,cors过来的请求根本没法做校验,很可能会出安全事故啊~ w3c的同学都不是吃素的,早就为你考虑到啦!一般来说,跨域的ajax请求是不带cookie的,而cors的ajax请求就不一样了,人家可以带cookie去服务端,这样就可以让服务端来判断这个请求是否合法。而且让ajax带cookie很简单,一句话的事儿:xhr.withCredentials = true;
。若服务端认为这个请求是合法的,返回的响应头里必须带上Access-Control-Allow-Credentials: true
,若为其他值或不带这个字段,浏览器会把withCredentials
发出的请求的响应通通拒绝掉。
这项技术的浏览器兼容情况还是不错的,IE8 ~ IE9使用XDomainRequest
,其他浏览器都使用XMLHttpRequest原生支持。
koa-cors
说完了cors和cors的原理,我们接着来讲koa-cors。
使用
var koa = require('koa');
var cors = require('kcors');
var app = koa();
app.use(cors());
cors方法可以传入一个对象options
{
origin:允许发来请求的域名,对应响应的`Access-Control-Allow-Origin`,
allowMethods:允许的方法,默认'GET,HEAD,PUT,POST,DELETE',对应`Access-Control-Allow-Methods`,
exposeHeaders: 允许客户端从响应头里读取的字段,对应`Access-Control-Expose-Headers`,
allowHeaders:这个字段只会在预请求的时候才会返回给客户端,标示了哪些请求头是可以带过来的,对应`Access-Control-Allow-Headers`,
maxAge:也是在预请求的时候才会返回,标明了这个预请求的响应所返回信息的最长有效期,对应`Access-Control-Max-Age`
credentials:标示该响应是合法的,对应`Access-Control-Allow-Credentials`
}
var requestOrigin = this.get('Origin');
if (!requestOrigin) {
return yield* next;
}
如请求头不带Origin字段,说明根本不是cors请求,直接忽略。
if (this.method !== 'OPTIONS') {
...
} else {
...
if (!this.get('Access-Control-Request-Method')) {
// this not preflight request, ignore it
return yield* next;
}
...
}
若该请求不是OPTIONS
方法,说明不是预请求,则根据options中的字段设置响应头,若该方法是OPTIONS
方法,但不带Access-Control-Request-Method
,说明不是预请求,仍然直接忽略。
this.status = 204;
在设置完需要设置的字段后,由于不需要返回具体内容,到这里就可以直接返回个204了。204表示不带响应体的成功响应。
什么是跨域的描述太表面,可以继续深挖。
对于代码的解读,应该直接贴代码,只说第几行根本不知道在说啥。代码一旦变动,行号就对应不上了
ok 已更新