在工作的一段时间里,给我最大的感触就是:公司有非常多的子系统,而且子系统与子系统之间交互非常的频繁,虽然公司拥有封装了HttpClient及增加了路由功能的R系统(该系统目的就是满足现下系统与系统之间通信频繁的问题),但是大家都知道HttpClient只限于服务器端使用,当我们想用客户端脚本,比如Javascript来调用其他系统(不同域)的接口时,就会出现跨域通信的问题。那么为什么会出现跨域通信问题呢?当遇到跨域通信问题时,我们又有几种解决方案呢?另外,跨域通信又会引发哪些潜在的问题呢?下面我将慢慢解释这几个问题。
一、产生跨域通信问题的本质原因
"无规矩不成方圆",这句话大家都知道,当我们在互联网的浪潮中冲浪的时候,我们无需考虑任何安全问题。因为互联网为了网民的安全,定制了很多策略(规矩),有一种策略叫同源策略,是每一种浏览器都必须实现并遵守的!那么同源策略到底限制了什么呢?
同源策略规定:不同域的客户端脚本在没有明确授权的情况下,不能读写对方的资源(好比你不能碰别人的女朋友一样),这里有几个关键字很有必要解释一下。
1、域、子域、同域
所谓同域(源)指:当A域与B域具有相同的协议、域名、端口时称为同域(源)
2、客户端脚本:由浏览器解释和执行的脚本语言,比如现下流行的Javascript,ActionScript,之前流行的VBScript
3、授权:服务端可以对客户端的访问进行授权,那么客户端也可以授权,比如HTML5中新标准中,当目标站点返回HTTP响应头Access-Control-Allow-Origin:http://www.cnblogs.com(A),那么浏览器就允许A站点去访问目标站点。
4、读写权限:Web上的资源很多,比如HTTP请求头的Referer只读,cookie即可写亦可读
5、资源:资源是个广泛的概念,只要是数据都可以认为是资源。同源策略里的资源特指Web客户端的资源(HTTP头,DOM树..)
在这里特别要注意的是,同源策略虽然限制了客户端的资源,但对静态的资源文件是没有加以限制的,例如客户端脚本文件,样式CSS文件、图片,flash资源等静态文件。(<script src=""><img src=""> <link rel=""><iframe src="">),但如果A域的js,想修改B域的dom元素也会造成跨域问题(2014.4.28补充)
二、为什么需要同源策略限制
我们举例说明:比如一个黑客程序,他利用iframe把真正的银行登录页面嵌到他的页面上,当你使用真实的用户名,密码登录时,他的页面就可以通过Javascript读取到你的表单中input中的内容,这样用户名,密码就轻松到手了。如果没有该策略的限制,后果可想而知。
三、同源策略的应用场景
前面介绍了同源策略的规定其存在的价值,那么浏览器的同源策略究竟在哪些场景会起作用呢?下面的三种场景是比较常见的!
1、窗口与窗口(不同域)之间的交互
2、iframe嵌入了不同域的资源(浏览器并不限制iframe嵌入不同域的资源,但限制客户端脚本去访问)
3、AJax应用(异步),请求不同域的资源
四、绕过同源策略解决跨域通信的方式
互联网的发展催生了跨域通信的需求,各种跨域方法和协议满足了需求但也增加了各种风险(风险下面会讲)。尤其是现在mashup的盛行。那么怎么去绕过浏览器的同源策略呢?下面我会讲在实际应用中比较常用的绕过同源策略的方式,这些方式会分为:客户端技术和服务端技术。但这两种方式的本质就是利用同源策略的一个漏洞(同源策略虽然限制了客户端的资源,但对静态的资源文件是没有加以限制的)和浏览器中不能直接来跨域访问,而在服务器端没有跨域安全限制(可以在服务端完成跨域访问,而在客户端来取得结果)。客户端技术利用的是前者,而服务端技术利用的是后者。
客户端技术
- iframe + document.domain(仅适用于子域之间,例如bbs.pclady.com.cn和www.pclady.com.cn)
- iframe(其原理:在iframe加载新页面时,window.name的值是保持不变的,由此可以重定向iframe的引用地址,由外域转到本域) -> JQuery的window.name插件(封装了iframe的实现) -- 支持post请求
- 动态Script标签->Jsonp -> JQuery的$.getScript(弊端只支持get请求,没有差错控制机制)
- HTML5 postMessage(需要现代的浏览器才兼容,像之前的ie6不兼容)
- Flash + crossdomain跨域(需要用户安装flash,在互联网应用里用户体验不好)
1、动态Script标签,返回的数据格式必须是text/javascript(并非一定是JSON,可以是任何数据类型),跨域通信盛行之后,慢慢就产生了JSONP的概念(就是将动态插入Script标签的技术美称为JSONP,但网上却有一大堆关于JSONP的解释,简直是误导众生啊!)
1 <html> 2 <head> 3 <title>How Many Pictures Of Madonna Do We Have?</title> 4 <script type="text/javascript"> 5 // 回调函数 6 function ws_results(obj) 7 { 8 // obj的格式可以是任意数据类型,比如JSON,字符串,xml 9 // 我们只需要相应的解析即可 10 // 服务器端的返回格式一定得是这样 ws_results(obj); 11 alert(obj.ResultSet.totalResultsAvailable); 12 } 13 14 function onClick() 15 { 16 var script = document.createElement("script"); 17 script.type = "text/javascript"; 18 script.src = "http://search.yahooapis.com/ImageSearchService/V1/imageSearch?appid=YahooDemo&query=Madonna&output=json&callback=ws_results"; 19 document.body.appendChild(script); 20 } 21 </script> 22 </head> 23 <body> 24 <input type="button" value="click me!" onclick="onClick()"> 25 </body> 26 </html>