Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

cors和koa-cors #11

Open
brunoyang opened this issue Dec 25, 2015 · 2 comments
Open

cors和koa-cors #11

brunoyang opened this issue Dec 25, 2015 · 2 comments

Comments

@brunoyang
Copy link
Owner

brunoyang commented Dec 25, 2015

近期在工作中用到了cors,所以写下这篇分享,本文基于koa-cors 1.0.1

啥是cors呢

Cross-Origin Resource Sharing(cors),顾名思义,跨域资源共享,也就是一种实现跨域的手段,想要的解决的问题是跟使用jsonp一样的。要想知道跨域是为了什么,得先知道什么是跨域。跨域(cross-orgin)是因为有同源策略(same-origin policy)的存在。浏览器为了保证加载的脚本等资源都是可控安全的,就加了一个强制性的规定,非同域名下的资源不得加载。同域名是指两个域名之间,端口,协议,以及子域都应相同

这是与http://www.alipay.com的通信结果

域名 结果 原因
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方法的请求,来『探测』一下我们的请求服务端接不接受,当然这一步是浏览器自动的,无需手动。『特殊情况』就是指下面的两种情况:

  • 使用了getheadpost之外的方法,或post请求的Content-Typeapplication/x-www-form-urlencodedmultipart/form-datatext/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表示不带响应体的成功响应。

@antife-yinyue
Copy link

什么是跨域的描述太表面,可以继续深挖。

对于代码的解读,应该直接贴代码,只说第几行根本不知道在说啥。代码一旦变动,行号就对应不上了

@brunoyang
Copy link
Owner Author

ok 已更新

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants