场景
跨域请求是随着应用服务化而越来越多遇到的问题,大体分为两类
1. 子域间通信:a.t.com 要和 b.t.com 通信
2. 完全不同域间通信:t.com 要和 m.com 通信
1 可以看做是 2 的特例,不过解决方法更简单点.
解决方案
完全不同域间通信
w3c 很早就考虑到了这种需求,提出了 Cross-Origin Resource Sharing 标准,通过定义一系列请求头和响应头,可以在客户端透明(或者经过很少的修改)得支持跨源的 xmlhttprequest。
服务器通过返回响应头进行权限控制,例如
Access-Control-Allow-Origin 控制那些外部请求可以访问该资源
Access-Control-Allow-Credentials 结合客户端 xmlhttprequest 的 withCredentials 属性可以控制是否发送 cookie 等验证信息
Access-Control-Allow-Headers 控制客户端可以发送的额外头部信息.
那么只要 b.t.com 的响应设置合适的头部信息,最好情况下 a.t.com 可以不经过任何修改就可以向 b.t.com 发请求.
ie 的例外
不出预料,ie 不完全支持此规范:
ie>=8
有自己的一套跨域请求机制 XDomainRequest ,通过替换 XmlHttpRequest 为 XDomainRequest 也可以往外部域发请求,但服务器端控制就少点,只能设置
Access-Control-Allow-Origin 控制那些外部请求可以访问该资源
也就意味着:不能发送 cookie 信息, 不能设置额外请求头。
ie<8
则是完全不支持,流行的做法是 flash 模拟,安全则交给 crossdomain.xml 控制,目前看来, ie<8 且不装 flash 的确实很少,可行!凑巧的是 flash 在 ie 下携带 cookie 信息,在其他浏览器下则不携带 cookie 信息,如果一定要附带 cookie 信息,那 ie 全平台都要用 flash 方案了。
另一种思路:
jsonp 不多说,最简单的一种,虽然控制少点(但服务器通过 refer 仍然可以限制来源请求)以及不能 post 数据(写操作受限),也算一个不错的选择。
子域间请求
子域访问作为跨域访问的特例,上述方法的任意一种都可行,但由于请求双方间共享一个主域,因而存在另外一种方案:
cross sub domain xmlhttprequest using proxy page
场景:
a.t.com 希望发请求给 b.t.com 的资源地址,但 b.t.com 的资源实际上只能通过 b.t.com 下的请求才能访问,而我们知道通过设置
document.domain = "t.com" ;
那么 a.t.com 就可以操作 b.t.com 的文档以及 window 对象。
具体做法为 引入 b.t.com/proxy.htm,内容为
<script>
document.domain="t.com"
</script>
将其作为 iframe 嵌入到欲发请求的 a.t.com 页面,a.t.com 通过操纵 b.t.com/prxoy.html 自身的 xmlhttprequest 向 b.t.com 发请求,这样子就可以绕过 a.t.com 自己的 xmlhttprequest 同源限制了.
问题
domain 设置是不可逆的,一旦主页面设置了 domain,那么其包含的iframe除非设置和主页面相同的 domain,否则就不能再和主页面通信,会导致大量的已有代码修改。
推荐:一开始进行全局总体设计时就规定所有子域页面统统设置 domain 为主域(全局脚本统一设置?)。
All In One
通过统一的接口,实际上可以做出透明的请求发送客户端,调用者不需要考虑以上细节,只要指定请求地址,通过工厂模式系统自动生成合适的客户端进行处理.