在客户端编程语言中,如javascript和ActionScript,同源策略是一个很重要的安全理念,它在保证数据的安全性方面有着重要的意义。同 源策略规定跨域之间的脚本是隔离的,一个域的脚本不能访问和操做另一个域的绝大部分属性和方法。那么什么叫相同域,什么叫不一样的域呢?javascript
同源策略php
在客户端编程语言中,如javascript和 ActionScript,同源策略是一个很重要的安全理念,它在保证数据的安全性方面有着重要的意义。同源策略规定跨域之间的脚本是隔离的,一个域的脚本不能访问和操做另一个域的绝大部分属性和方法。那么什么叫相同域,什么叫不一样的域呢?当两个域具备相同的协议(如http), 相同的端口(如80),相同的host(如),那么咱们就能够认为它们是相同的域。好比 和是同域,而, , , 中的任何两个都将构成跨域。同源策略还应该对一些特殊状况作处理,好比限制file协议下脚本的访问权限。本地的HTML文件在浏览器中是经过file协议打开的,若是脚本能经过file协议访问到硬盘上其它任意文件,就会出现安全隐患,目前IE8还有这样的隐患。html
受到同源策略的影响,跨域资源共享就会受到制约。可是随着人们的实践和浏览器的进步,目前在跨域请求的技巧上,有不少宝贵经验的沉淀和积累。这里我把跨域资源共享分红两种,一种是单向的数据请求,还有一种是双向的消息通讯。接下来我将罗列出常见的一些跨域方式,如下跨域实例的源代码能够从这里得到。java
单向跨域编程
JSONP跨域
JSONP (JSON with Padding)是一个简单高效的跨域方式,HTML中的script标签能够加载并执行其余域的javascript,因而咱们能够经过script标记来动态加载其余域的资源。例如我要从域A的页面pageA加载域B的数据,那么在域B的页面pageB中我以JavaScript的形式声明pageA须要的数据,而后在 pageA中用script标签把pageB加载进来,那么pageB中的脚本就会得以执行。JSONP在此基础上加入了回调函数,pageB加载完以后会执行pageA中定义的函数,所须要的数据会以参数的形式传递给该函数。JSONP易于实现,可是也会存在一些安全隐患,若是第三方的脚本随意地执行,那么它就能够篡改页面内容,截获敏感数据。可是在受信任的双方传递数据,JSONP是很是合适的选择。浏览器
flash URLLoader安全
flash有本身的一套安全策略,服务器能够经过crossdomain.xml文件来声明能被哪些域的SWF文件访问,SWF也能够经过API来肯定自身能被哪些域的SWF加载。当跨域访问资源时,例如从域请求域上的数据,咱们能够借助flash来发送HTTP请求。首先,修改域上的crossdomain.xml(通常存放在根目录,若是没有须要手动建立) ,把加入到白名单。其次,经过Flash URLLoader发送HTTP请求,最后,经过Flash API把响应结果传递给JavaScript。Flash URLLoader是一种很广泛的跨域解决方案,不过须要支持iOS的话,这个方案就无能为力了。服务器
Access Controldom
Access Control是比较超越的跨域方式,目前只在不多的浏览器中得以支持,这些浏览器能够发送一个跨域的HTTP请求(Firefox, Google Chrome等经过XMLHTTPRequest实现,IE8下经过XDomainRequest实现),请求的响应必须包含一个Access- Control-Allow-Origin的HTTP响应头,该响应头声明了请求域的可访问权限。例如对下的 asset.php发送了一个跨域的HTTP请求,那么asset.php必须加入以下的响应头:
header("Access-Control-Allow-Origin: ");
window.name
window 对象的name属性是一个很特别的属性,当该window的location变化,而后从新加载,它的name属性能够依然保持不变。那么咱们能够在页面 A中用iframe加载其余域的页面B,而页面B中用JavaScript把须要传递的数据赋值给window.name,iframe加载完成以后,页面A修改iframe的地址,将其变成同域的一个地址,而后就能够读出window.name的值了。这个方式很是适合单向的数据请求,并且协议简单、安全。不会像JSONP那样不作限制地执行外部脚本。
server proxy
在数据提供方没有提供对JSONP协议或者 window.name协议的支持,也没有对其它域开放访问权限时,咱们能够经过server proxy的方式来抓取数据。例如当域下的页面须要请求下的资源文件asset.txt时,直接发送一个指向 /asset.txt的Ajax请求确定是会被浏览器阻止。这时,咱们在下配一个代理,而后把Ajax请求绑定到这个代理路径下,例如/proxy/, 而后这个代理发送HTTP请求访问下的asset.txt,跨域的HTTP请求是在服务器端进行的,客户端并无产生跨域的Ajax请求。这个跨域方式不须要和目标资源签定协议,带有侵略性,另外须要注意的是实践中应该对这个代理实施必定程度的保护,好比限制他人使用或者使用频率。
双向跨域
document.domain
经过修改document的domain属性,咱们能够在域和子域或者不一样的子域之间通讯。同域策略认为域和子域隶属于不一样的域,好比和 su是不一样的域,这时,咱们没法在下的页面中调用su中定义的JavaScript方法。可是当咱们把它们document的domain属性都修改成a.com,浏览器就会认为它们处于同一个域下,那么咱们就能够互相调用对方的method来通讯了。
FIM – Fragment Identitier Messaging
不一样的域之间,JavaScript只能作颇有限的访问和操做,其实咱们利用这些有限的访问权限就能够达到跨域通讯的目的了。FIM (Fragment Identitier Messaging)就是在这个大前提下被发明的。父窗口能够对iframe进行URL读写,iframe也能够读写父窗口的URL,URL有一部分被称为frag,就是#号及其后面的字符,它通常用于浏览器锚点定位,Server端并不关心这部分,应该说HTTP请求过程当中不会携带frag,因此这部分的修改不会产生HTTP请求,可是会产生浏览器历史记录。FIM的原理就是改变URL的frag部分来进行双向通讯。每一个window经过改变其余 window的location来发送消息,并经过监听本身的URL的变化来接收消息。这个方式的通讯会形成一些没必要要的浏览器历史记录,并且有些浏览器不支持onhashchange事件,须要轮询来获知URL的改变,最后,URL在浏览器下有长度限制,这个制约了每次传送的数据量。
Flash LocalConnection
页面上的双向通讯也能够经过Flash来解决,Flash API中有LocalConnection这个类,该类容许两个SWF之间经过进程通讯,这时SWF能够播放在独立的Flash Player或者AIR中,也能够嵌在HTML页面或者是PDF中。遵循这个通讯原则,咱们能够在不一样域的HTML页面各自嵌套一个SWF来达到相互传递数据的目的了。SWF经过LocalConnection交换数据是很快的,可是每次的数据量有40kb的大小限制。用这种方式来跨域通讯过于复杂,并且须要了2个SWF文件,实用性不强。
window.postMessage
window.postMessage是HTML5定义的一个很新的方法,这个方法能够很方便地跨window通讯。因为它是一个很新的方法,因此在很旧和比较旧的浏览器中都没法使用。
Cross Frame
Cross Frame是FIM的一个变种,它借助了一个空白的iframe,不会产生多余的浏览器历史记录,也不须要轮询URL的改变,在可用性和性能上都作了很大的改观。它的基本原理大体是这样的,假设在域上有页面A.html和一个空白代理页面proxyA.html, 另外一个域上有个页面B.html和一个空白代理页面proxyB.html,A.html须要向B.html中发送消息时,页面会建立一个隐藏的iframe, iframe的src指向proxyB.html并把message做为URL frag,因为B.html和proxyB.html是同域,因此在iframe加载完成以后,B.html能够得到iframe的URL,而后解析出 message,并移除该iframe。当B.html须要向A.html发送消息时,原理同样。Cross Frame是很好的双向通讯方式,并且安全高效,可是它在Opera中没法使用,不过在Opera下面咱们可使用更简单的 window.postMessage来代替。
总结
跨域的方法不少,不一样的应用场景咱们均可以找到一个最合适的解决方案。好比单向的数据请求,咱们应该优先选择JSONP或者window.name,双向通讯咱们采起Cross Frame,在未与数据提供方没有达成通讯协议的状况下咱们也能够用server proxy的方式来抓取数据。