做 Web 开发经常需要面对跨域问题,跨域问题的根源是浏览器安全中的同源策略,比如说,对于 http://www.a.com/1.html 来说:
在浏览器中,<script>、<img>、<iframe> 和<link> 这几个标签是可以加载跨域(非同源)的资源的,并且加载的方式其实相当于一次普通的 GET 请求,唯一不同的是,为了安全起见,浏览器不允许这种方式下对加载到的资源的读写操作,而只能使用标签本身应当具备的能力(比如脚本执行、样式应用等等)。
最常见的跨域问题是 Ajax 跨域访问的问题,默认情况下,跨域的 URL 是无法通过 Ajax 访问的。这里我记录我所了解到的跨域的方法:
这两者都最终实现了跨域的调用,这个方法功能上要比下面介绍到的 JSONP 更强,因为跨域完毕之后 DOM 操作和互相之间的 JavaScript 调用都是没有问题的,但是也有一些限制,比如结果要以 URL 参数传递,这就意味着在结果数据量很大的时候需要分割传递,甚是麻烦;还有一个麻烦是 iframe 本身带来的,母页面和 iframe 本身的交互本身就有安全性限制。
在互联网上有很多 JSONP 的服务来提供数据,本质上就是跨域请求,并且在请求 URL 中指定好 callback,比如 callback=result,那么在获取到这些数据以后,就会自动调用 result 函数,并且把这些数据以 JSON 的形式传进去,例如(搜索“football”):
http://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=football&callback=result
使用 JQuery 来调用就写成:
$.getJSON("http://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=football&callback=?",function(data){
//...
});
总的来说,JSONP 的跨域方式的局限性在于,只能使用 GET 请求,并且不能解决不同域的两个页面之间如何进行 JavaScript 调用的问题。
4. Flash 跨域:
它会访问目标网站根目录下面的 crossdomain.xml 文件,根据文件中的内容来确定是否允许此次跨域访问:
<cross-domain-policy>
<allow-access-from domain="xxx.xxx.com" />
</cross-domain-policy>
otherWindow.postMessage(message, targetOrigin);
在接收的窗口,需要设置一个事件处理函数来接收发过来的消息:
window.addEventListener("message", receiveMessage, false);
function receiveMessage(event){
if (event.origin !== "http://example.org:8080")
return;
}
注意这里必需要使用消息的 origin 和 source 属性来验证发送者的身份,否则会造成 XSS 漏洞。
有一些浏览器支持 Access-Control-Allow-Origin 这样的响应头,比如:
header("Access-Control-Allow-Origin: http://www.a.com");
就指定了允许对 www.a.com 跨域访问。
这个东西其实以前被用作黑客 XSS 的手段,其本质是,当 window 的 location 变化的时候,页面会重新加载,但是有趣的是,这个 window.name 居然不发生变化,那么就可以用它来传值了。配合 iframe,改变几次 iframe 的 window 对象,就完成了实用的跨域数据传递。
这个方式适用于 a.example.com 和 b.example.com 这种跨域的通信,因为二者有一个共有的域,叫做 example.com,只要设置 document.domain 为 example.com 就可以了,但是如果 a.example1.com 和 b.example2.com 之间要通信,它就没办法了。
10. Fragment Identitier Messaging(FIM)
这个方法很有意思,也需要 iframe 的配合。Fragment Identitier 就是 URL 的井号(#)后面的经常用于锚点定位的部分,这部分的改变不会导致页面刷新,母窗口可以随便访问 iframe 的 URL,而 iframe 也可以随便访问母窗口的 URL,那这二者之间就可以通过改变 Fragmement Identitier 来实现通信了。缺点是 Fragmement Identitier 的改变会产生不必要的历史记录,而且也有长度限制;另外,有的浏览器不支持 onhashchange 事件。
这种方法是上述 FIM 方法的变种,CF 和 FIM 的本质其实在我的 《GWT 初体验》这篇文章里面都有介绍(只不过是被用来实现历史和后退功能了),它会动态创建一个不可见的 iframe,指向异域,处理完以后,这个 iframe 的 URL 中的 Fragment Identitier 包含了处理结果,供母页面访问,而浏览器的 URL 没有任何变化。
利用 P3P 协议下跨域访问 Cookie 的特性,来实现跨域访问,也算一奇招。P3P 是 W3C 公布的一项隐私保护推荐标准,旨在为网上冲浪的 Internet 用户提供隐私保护。把 Cookie 的 path 设置为“/”,即没有任何域的限制,这个时候有的浏览器下面允许别的 URL 的页面来读取,有的则不允许,这种情况下需要在母页面响应的头上面设置 P3P 的头:
P3P: CP="CURa ADMa DEVa PSAo PSDo OUR BUS UNI PUR INT DEM STA PRE COM NAV OTC NOI DSP COR"
文章未经特殊标明皆为本人原创,未经许可不得用于任何商业用途,转载请保持完整性并注明来源链接 《四火的唠叨》