CORS笔记

source: http://laogao.me/blog/2014/08/23/cors-notes/

相信有不少服务端开发同学和我类似,初次接触CORS的体验是这样的:你这个服务调不通啊,“跨域”了,要打开CORS。什么?CORS干嘛的?然后有人告知你要在服务端响应header里加上这个、这个和这个,就好了,几乎是口口相传,代码拷贝粘帖,然后bingo,然后就没有然后了。

作为有情怀的服务端开发,怎么甘心被前端同学这么牵着鼻子走呢你说是不是?DISCLAMER:想全面学习CORS请考虑购买《CORS in Action》[1],目前最好的介绍CORS的书。这里只是简单结合实际项目中的心得做个笔记。

CORS的全称是Cross-Origin Resource Sharing,即跨源资源共享,常用于Web前端代码访问服务端暴露的API,比如去flickr加载图片之类。什么是“源”?为什么不说“跨域”?这里要澄清几个概念。首先,参与CORS的角色主要有:JavaScript代码(客户端)、浏览器、服务端。浏览器说白了就是给JavaScript的运行提供一个沙箱,为了保护最终用户的安全,随着网页打开而运行JavaScript代码能做的事情相当受限,其中一个重要的约束就是,JavaScript默认只能对同“源”的服务端资源发起请求。除非服务端特别允许,浏览器会拦截掉JavaScript的跨“源”请求。也就是说,要实现CORS,JavaScript代码、浏览器和目标服务端都必须主动参与,JavaScript要发起请求,浏览器加工header并允许调用,而服务端也必须声明允许来自某个或某些“源”的JavaScript请求,整个调用才会成功。注意这里特别强调的浏览器的作用,如果是用curl之类的命令行工具,CORS调用并没有“浏览器”的参与,也就没有来自浏览器的隐含限制,只有curl和服务端之间的交互,至少在curl执行时看起来并不会被自动带上CORS的痕迹,而浏览器通常会。

那么,对于浏览器而言,如何判断JavaScript对外发起的请求属于“同源”还是“不同源”呢?所谓的“源”,就是资源来自哪里,比如承载JavaScript的网页所在的站点,以及JavaScript试图访问的服务端地址,等等。构成“源”的信息包括:协议(http或https)、主机地址(比如laogao.me)、端口。如下是一些示例:

URL
http://localhost:9700 http://localhost:9700
http://localhost:9700/a.html http://localhost:9700
https://localhost:9443/login https://localhost:9443
http://127.0.0.1/some_api http://127.0.0.1

注意localhost和127.0.0.1不会被识别为相同的主机地址,浏览器几乎是靠文本匹配来判断网页地址和请求目标地址是否同“源”。比如在http://localhost:9700/a.html 这个网页上,有一段JavaScript,想对 http://127.0.0.1/some_api 发起请求,浏览器会认为这是跨“源”调用。

知道了浏览器如何判定跨“源”,对于那些我们希望成功的客户端对服务端资源的跨“源”请求,我们如何让浏览器和服务端允许呢?主要的开发工作还是在服务端:

  1. JavaScript客户端发起跨“源”请求。
  2. 浏览器给请求加上Origin头,内容为前面我们表格里看到的那样的“地址”。
  3. 服务端收到带Origin头,并且确实来自不同“源”的请求,如果允许,则需要在响应中加上HTTP头Access-Control-Allow-Origin: *,其中*表示所有源都允许,当然我们也可以明确指出我们允许某个具体的“源”。
  4. 浏览器截获该响应,判定服务端已允许来自发起调用的JavaScript代码所在“源”的请求,将结果返回给JavaScript客户端。

这是最简单的支持CORS的方式,只要服务端加上一个响应头Access-Control-Allow-Origin: *即可。不过现实情况很少会这么简单,通常服务端资源都不是完全公开的,因此需要某种限制或保护,比如仅针对某些特定“源”放开,甚至是查验cookie等。针对特定“源”相对好办,服务端判断一下,来自白名单的“源”,显示允许即可;查验cookie稍微麻烦点,除了客户端JavaScript调用时给XMLHttpRequest对象设置withCredentials为true之外,还需要服务端响应Access-Control-Allow-Credentials: true,如果不这样做,浏览器默认是不会在请求中带上cookie的。而一旦加上了Access-Control-Allow-Credentials: true,浏览器将不允许Access-Control-Allow-Origin: *这样的通配写法,而必须显式指出允许的“源”,且要与请求中Origin头的值一致。

说到这里可能很多人都会有疑问,都已经到响应阶段了,该带的cookie也都带了,这样的头有意义吗?其实前面我们忽略了一个细节,就是preflight请求。不同浏览器实现稍有不同,并且默认也不是每一类或每一个请求都做,但基本逻辑是:在实际的CORS请求发起之前,浏览器向服务端发起一个试探性的OPTIONS请求,询问服务端是否支持CORS以及支持哪些方法和哪些请求头,这些都确认OK之后,才发起真正的请求。如果我们想做精细控制,服务端就得感知这件些请求/调用细节,并作出正确合理的响应。

最后列一下最常见的CORS相关请求头/属性和响应头,看上去还挺工整的,使用时注意下应该不至于搞错:

请求头或属性 响应头 备注
Origin Access-Control-Allow-Origin 发起源和服务端允许的源
withCredentials Access-Control-Allow-Credentials 请求是否带cookie和服务端是否允许带
Access-Control-Request-Methods Access-Control-Allow-Methods 请求使用的HTTP方法和服务端允许的方法
Access-Control-Request-Headers Access-Control-Allow-Headers 请求带上头和服务端允许的头

[1] http://www.manning.com/hossain/

Share Comments

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值