`

[转]疯狂的跨域技术

阅读更多

JavaScript是一种在Web开发中经常使用的前端动态脚本技术。在JavaScript中,有一个很重要的安全性限制,被称为“Same-Origin Policy”(同源策略)。这一策略对于JavaScript代码能够访问的页面内容做了很重要的限制,即JavaScript只能访问与包含它的文档在同一域下的内容。

JavaScript这个安全策略在进行多iframe或多窗口编程、以及Ajax编程时显得尤为重要。根据这个策略,在baidu.com下的页面中包含的JavaScript代码,不能访问在google.com域名下的页面内容;甚至不同的子域名之间的页面也不能通过JavaScript代码互相访问。对于Ajax的影响在于,通过XMLHttpRequest实现的Ajax请求,不能向不同的域提交请求,例如,在abc.example.com下的页面,不能向def.example.com提交Ajax请求,等等。

然而,当进行一些比较深入的前端编程的时候,不可避免地需要进行跨域操作,这时候“同源策略”就显得过于苛刻。本文就这个问题,概括了跨域所需要的一些技术。

下面我们分两种情况讨论跨域技术:首先讨论不同子域的跨域技术,然后讨论完全不同域的跨域技术。

(一)不同子域的跨域技术。

我们分两个问题来分别讨论:第一个问题是如何跨不同子域进行JavaScript调用;第二个问题是如何向不同子域提交Ajax请求。

先来解决第一个问题,假设example.com域下有两个不同子域:abc.example.com和def.example.com。现在假设在def.example.com下面有一个页面,里面定义了一个JavaScript函数:

Js代码 复制代码
  1. function funcInDef() {   
  2.   
  3. .....   
  4.   
  5. }  
 

我们想在abc.example.com下的某个页面里调用上面的函数。再假设我们要讨论的abc.example.com下面的这个页面是以iframe形式嵌入在def.example.com下面那个页面里的,这样我们可能试图在iframe里做如下调用:

window.top.funcInDef();

好,我们注意到,这个调用是被前面讲到的“同源策略”所禁止的,JavaScript引擎会直接抛出一个异常。

为了实现上述调用,我们可以通过修改两个页面的domain属性的方法做到。例如,我们可以将上面在abc.example.com和def.example.com下的两个页面的顶端都加上如下的JavaScript代码片段:

Html代码 复制代码
  1. <script type="text/javascript">  
  2.   
  3. document.domain = "example.com";   
  4.   
  5. </script>  
 

这样,两个页面就变为同域了,前面的调用也可以正常执行了。

这里需要注意的一点是,一个页面的document.domain属性只能设置成一个更顶级的域名(除了一级域名),但不能设置成比当前域名更深层的子域名。例如,abc.example.com的页面只能将它的domain设置成example.com,不能设置成sub.abc.example.com,当然也不能设置成一级域名com。

上面的例子讨论的是两个页面属于iframe嵌套关系的情况,当两个页面是打开与被打开的关系时,原理也完全一样。

下面我们来解决第二个问题:如何向不同子域提交Ajax请求。

通常情况下,我们会用与下面类似的代码来创建一个XMLHttpRequest对象:

Js代码 复制代码
  1. factories = [   
  2.   
  3. function() { return new XMLHttpRequest(); },   
  4.   
  5. function() { return new ActiveXObject("Msxml2.XMLHTTP"); },   
  6.   
  7. function() { return new ActiveXObject("Microsoft.XMLHTTP"); }   
  8.   
  9. ];   
  10.   
  11. function newRequest() {   
  12.   
  13. for(var i = 0; i < factories.length; i++) {   
  14.   
  15. try{   
  16.   
  17. var factory = factories[i];   
  18.   
  19. return factory();   
  20.   
  21. catch(e) {}   
  22.   
  23. }   
  24.   
  25. return null;   
  26.   
  27. }  
上面的代码中引用ActiveXObject,是为了兼容IE6系列浏览器。每次我们调用newRequest函数,就获得了一个刚刚创建的Ajax对象,然后用这个Ajax对象来发送HTTP请求。例如,下面的代码向abc.example.com发送了一个GET请求:
Js代码 复制代码
  1. var request = newRequest();   
  2.   
  3. request.open("GET""http://abc.example.com" );   
  4.   
  5. request.send(null);  
 

假设上面的代码包含在一个abc.example.com域名下的页面里,则这个GET请求可以正常发送成功,没有任何问题。然而,如果现在要向def.example.com发送请求,则出现跨域问题,JavaScript引擎抛出异常。

解决的办法是,在def.example.com域下放置一个跨域文件,假设叫crossdomain.html;然后将前面的newRequest函数的定义移到这个跨域文件中;最后像之前修改document.domain值的做法一样,在crossdomain.html文件和abc.example.com域下调用Ajax的页面顶端,都加上:

Html代码 复制代码
  1. <script type="text/javascript">  
  2.   
  3. document.domain = "example.com";   
  4.   
  5. </script>  
 

为了使用跨域文件,我们在abc.example.com域下调用Ajax的页面中嵌入一个隐藏的指向跨域文件的iframe,例如:

Html代码 复制代码
  1. <iframe name="xd_iframe" style="display:none" src="http://def.example.com/crossdomain.html">  
  2. </iframe>  
  

这时abc.example.com域下的页面和跨域文件crossdomain.html都在同一个域(example.com)下,我们可以在abc.example.com域下的页面中去调用crossdomain.html中的newRequest函数:

Js代码 复制代码
  1. var request = window.frames["xd_iframe"].newRequest();  
 

这样获得的request对象,就可以向http://def.example.com发送HTTP请求了。

(二)完全不同域的跨域技术。

如果顶级域名都不相同,例如example1.com和example2.com之间想通过JavaScript在前端通信,则所需要的技术更复杂些。

在讲解不同域的跨域技术之前,我们首先明确一点,下面要讲的技术也同样适用于前面跨不同子域的情况,因为跨不同子域只是跨域问题的一个特例。当然,在恰当的情况下使用恰当的技术,能够保证更优的效率和更高的稳定性。

简言之,根据不同的跨域需求,跨域技术可以归为下面几类:

  1. JSONP跨域GET请求
  2. 通过iframe实现跨域
  3. flash跨域HTTP请求
  4. window.postMessage

下面详细介绍各种技术。

1. JSONP。

利用在页面中创建<script>节点的方法向不同域提交HTTP请求的方法称为JSONP,这项技术可以解决跨域提交Ajax请求的问题。JSONP的工作原理如下所述:

假设在http://example1.com/index.php这个页面中向http://example2.com/getinfo.php提交GET请求,我们可以将下面的JavaScript代码放在http://example1.com/index.php这个页面中来实现:

Js代码 复制代码
  1. var eleScript= document.createElement("script");   
  2.   
  3. eleScript.type = "text/javascript";   
  4.   
  5. eleScript.src = "http://example2.com/getinfo.php";   
  6.   
  7. document.getElementsByTagName("HEAD")[0].appendChild(eleScript);  
 

 

当GET请求从http://example2.com/getinfo.php返回时,可以返回一段JavaScript代码,这段代码会自动执行,可以用来负责调用http://example1.com/index.php页面中的一个callback函数。

JSONP的优点是:它不像XMLHttpRequest对象实现的Ajax请求那样受到同源策略的限制;它的兼容性更好,在更加古老的浏览器中都可以运行,不需要XMLHttpRequest或ActiveX的支持;并且在请求完毕后可以通过调用callback的方式回传结果。

JSONP的缺点则是:它只支持GET请求而不支持POST等其它类型的HTTP请求;它只支持跨域HTTP请求这种情况,不能解决不同域的两个页面之间如何进行JavaScript调用的问题。

2. 通过iframe实现跨域。

iframe跨域的方式,功能强于JSONP,它不仅能用来跨域完成HTTP请求,还能在前端跨域实现JavaScript调用。因此,完全不同域的跨域问题,通常采用iframe的方式来解决。

与JSONP技术通过创建<script>节点向不同的域提交GET请求的工作方式类似,我们也可以通过在http://example1.com/index.php页面中创建指向http://example2.com/getinfo.php的iframe节点跨域提交GET请求。然而,请求返回的结果无法回调http://example1.com/index.php页面中的callback函数,因为受到“同源策略”的影响。

为了解决这个问题,我们需要在example1.com下放置一个跨域文件,比如路径是http://example1.com/crossdomain.html。

当http://example2.com/getinfo.php这个请求返回结果的时候,它大体上有两个选择。

第一个选择是,它可以在iframe中做一个302跳转,跳转到跨域文件http://example1.com/crossdomain.html,同时将返回结果经过URL编码之后作为参数缀在跨域文件URL后面,例如http://example1.com/crossdomain.html?result=<URL-Encoding-Content>。

另一个选择是,它可以在返回的页面中再嵌入一个iframe,指向跨域文件,同时也是将返回结果经过URL编码之后作为参数缀在跨域文件URL后面。

在跨域文件中,包含一段JavaScript代码,这段代码完成的功能,是从URL中提取结果参数,经过一定处理后调用原来的http://example1.com/index.php页面中的一个预先约定好的callback函数,同时将结果参数传给这个函数。http://example1.com/index.php页面和跨域文件是在同一个域下的,因此这个函数调用可以通过。跨域文件所在iframe和原来的http://example1.com/index.php页面的关系,在前述第一种选择下,后者是前者的父窗口,在第二种选择下,后者是前者的父窗口的父窗口。

根据前面的叙述,有了跨域文件之后,我们就可以实现通过iframe方式在不同域之间进行JavaScript调用。这个调用过程可以完全跟HTTP请求无关,例如有些站点可以支持动态地调整在页面中嵌入的第三方iframe的高度,这其实是通过在第三方iframe里面检测自己页面的高度变化,然后通过跨域方式的函数调用将这个变化告知父窗口来完成的。

既然利用iframe可以实现跨域JavaScript调用,那么跨域提交POST请求等其它类型的HTTP请求就不是难事。例如我们可以跨域调用目标域的JavaScript代码在目标域下提交Ajax请求(GET/POST/etc.),然后将返回的结果再跨域传原来的域。

使用iframe跨域,优点是功能强大,支持各种浏览器,几乎可以完成任何跨域想做的事情;缺点是实现复杂,要处理很多浏览器兼容问题,并且传输的数据不宜过大,过大了可能会超过浏览器对URL长度的限制,要考虑对数据进行分段传输等。

3. 利用flash实现跨域HTTP请求

据称,flash在浏览器中的普及率高达90%以上。

flash代码和JavaScript代码之间可以互相调用,并且flash的“安全沙箱”机制与JavaScript的安全机制并不尽相同,因此,我们可以利用flash来实现跨域提交HTTP请求(支持GET/POST等)。

例如,我们用浏览器访问http://example1.com/index.php这个页面,在这个页面中引用了http://example2.com/flash.swf这个flash文件,然后在flash代码中向http://example3.com/webservice.php发送HTTP请求。

这个请求能否被成功发送,取决于在example3.com的根路径下是否放置了一个crossdomain.xml以及这个crossdomain.xml的配置如何。flash的“安全沙箱”会保证:仅当example3.com服务器在根路径下确实放置了crossdomain.xml文件并且在这个文件中配置了允许接受来自example2.com的flash的请求时,这个请求才能真正成功。下面是一个crossdomain.xml文件内容的例子:

Xml代码 复制代码
  1. <?xml version="1.0"?>  
  2.   
  3. <cross-domain-policy>  
  4.   
  5. <allow-access-from domain="example2.com" />  
  6.   
  7. </cross-domain-policy>  
 

4. window.postMessage

window.postMessage是HTML标准的下一个版本HTML5支持的一个新特性。受当前互联网技术突飞猛进的影响,浏览器跨域通信的需求越来越强烈,HTML标准终于把跨域通信考虑进去了。但目前HTML5仍然只是一个draft。

window.postMessage是一个安全的实现直接跨域通信的方法。但是目前并不是所有浏览器都能支持,只有Firefox 3、Safari 4和IE8可以支持这个调用。

使用它向其它窗口发送消息的调用方式大概如下:

Js代码 复制代码
  1. otherWindow.postMessage(message, targetOrigin);  
在接收的窗口,需要设置一个事件处理函数来接收发过来的消息:
Js代码 复制代码
  1. window.addEventListener("message", receiveMessage, false);   
  2. function receiveMessage(event){   
  3.  if (event.origin!== "http://example.org:8080"return;}  
 

消息包含三个属性:data、origin(携带发送窗口所在域的真实信息)和source(代表发送窗口的handle)。

安全性考虑:使用window.postMessage,必需要使用消息的origin和source属性来验证发送者的身份,否则会造成XSS漏洞。

window.postMessage在功能上同iframe实现的跨域功能同样强大,并且使用简单,效率更高,但缺点是它目前在浏览器兼容方面有待提高。

 

总结完所有的跨域方式之后,我们要时刻铭记,虽然跨域技术能给你带来更多的功能,催生出更灵活和更加平台化的产品,但是功能的放开也总是意味着安全的风险。在实现跨域技术的每个步骤和细节,都要时刻在头脑中考虑到对安全带来的影响,避免成为XSS攻击的漏洞。

分享到:
评论

相关推荐

    跨域引用资源技术

    跨域引用资源技术是Web开发中的一个重要概念,它涉及到浏览器的同源策略和资源的共享。同源策略是由浏览器强制实施的一种安全策略,确保JavaScript只能访问与当前页面同源(即相同协议、相同域名和相同端口)的网页...

    cors技术解决ajax跨域

    **CORS技术详解:解决AJAX跨域问题** 在Web开发中,由于浏览器的同源策略限制,AJAX请求只能向同一源(协议+域名+端口)发送,这在跨域请求时会遇到障碍。为了解决这个问题,CORS(Cross-Origin Resource Sharing,...

    面向车联网的MEC跨域协同技术研究

    使用场景及目标:研究车联网跨域技术,实现不同边缘节点之间数据和服务无缝迁移的目标,最终达成跨运营商MEC的高效、低延迟的车辆通讯服务连续性保障。 其它说明:目前这一技术还处在起步发展的阶段,许多标准仍未...

    finereport跨域打印文档

    【Finereport跨域打印文档】是指在使用帆软报表工具Finereport时,涉及到的在不同源(即跨域)环境下进行文档打印的技术说明。跨域打印是Web开发中的一个重要概念,通常涉及到浏览器的同源策略限制。由于安全原因,...

    国家电子健康卡建设指南(2.5);居民健康卡跨域主索引系统技术指引

    《国家电子健康卡建设指南(2.5)》与《居民健康卡跨域主索引系统技术指引》是关于中国居民健康信息化的重要指导文件,旨在推动全国统一的电子健康卡应用,解决医疗健康领域的信息孤岛问题。这些指南强调了采用国密...

    jquery跨域调用 js跨域调用

    五、其他跨域技术 1. 代理服务器:如果服务器不支持JSONP或CORS,可以通过在服务器上设置一个代理,将跨域请求转发到目标服务器。 2. HTML5的Window.postMessage:适用于两个窗口之间的跨域通信,如弹窗或iframe。 ...

    JavaScript两种跨域技术全面介绍

    跨域技术大致可以分为两类:不同子域的跨域技术以及完全不同域的跨域技术。 首先,讨论不同子域的跨域技术。假设我们有两个子域***和***,需要在这两个域之间进行数据交换。按照同源策略,这两者属于不同的源,不能...

    完美解决iframe跨域问题

    在互联网应用中,由于同源策略的限制,不同域名、协议或端口的网页之间无法直接进行通信。然而,有时我们需要在不同的域之间共享...在实际开发中,结合其他跨域技术,如`postMessage`,可以构建更健壮的跨域通信系统。

    H5的video标签跨域.HTML的video标签跨域_跨域ajax

    H5的video标签跨域.HTML的video标签跨域 我们都知道HTML video标签能播放视频 但是如果你的video要播放的是非当前域名下的视频文件,这就要跨域播放视频, 应该如何实现呢?

    跨域安全JAR包

    跨域安全JAR包是一种专为解决Web应用中跨域问题而设计的工具包,它旨在简化跨域请求的处理,使开发者无需依赖传统的JSONP(JSON with Padding)技术即可实现安全的数据交换。JSONP是一种早期的跨域数据交互协议,它...

    ajax跨域请求WebService.asmx

    【Ajax跨域请求WebService.asmx】是一个常见的Web开发技术应用场景,主要涉及到JavaScript的Ajax技术、C#编程语言以及ASP.NET的WebService组件。Ajax(Asynchronous JavaScript and XML)是一种在不刷新整个页面的...

    Cesium加载Geoserver跨域问题

    3. 配置CORS策略:转到"Data" -&gt; "Stores",选择你想要允许跨域的数据存储,点击"Edit"。在右侧的"Advanced Settings"(高级设置)部分,找到"CORS Configuration",在这里可以设置允许跨域的源(如`*`表示所有源)...

    MVC中跨域解决方案

    然而,随着Web应用的发展,跨域问题常常成为开发过程中的一个挑战。"跨域"是指由于浏览器的安全策略,一个源(Origin,由协议、域名和端口组成)的文档不能通过JavaScript直接访问另一个源的资源。为了解决这个问题...

    js跨域解决方案

    JavaScript跨域是Web开发中一个常见的挑战,由于浏览器的同源策略限制,JavaScript无法直接访问不同源(协议、域名或端口不一致)的资源。本文将深入探讨JS跨域的解决方案,帮助开发者理解并解决这个问题。 首先,...

    电子健康卡技术规范 跨域索引认证(2019年版).pdf

    在深入探讨电子健康卡技术规范跨域索引认证(2019年版)之前,需要明确几个核心概念。首先,电子健康卡作为患者身份识别和健康信息记录的电子化工具,在医疗卫生领域扮演着重要角色。跨域索引认证则是确保电子健康卡...

    普元eos跨域组件

    3. **前端集成**:在前端代码中,根据选用的跨域技术(JSONP或CORS),编写相应的请求逻辑。如果是JSONP,需要定义回调函数;如果是CORS,通常只需要发起请求即可,由服务器处理跨域问题。 4. **测试验证**:最后,...

    Jquery跨域Ajax请求测试

    在Web开发中,由于同源策略的限制,JavaScript通常无法直接...理解并熟练掌握这些技术,对于构建现代Web应用至关重要。在实际项目中,根据具体需求和安全考虑选择合适的方法,确保数据能够安全有效地在不同域之间传输。

    利用动态创建script技术解决静态网站跨域问题.pdf

    解决静态网站跨域问题的动态创建Script技术 本文介绍了一种解决静态网站跨域问题的技术,即利用动态创建Script技术。该技术可以解决静态网站在跨域访问时的安全隐患问题。 动态创建Script技术是指在客户端生成...

    Vue中跨域及打包部署到nginx跨域设置方法

    众所周知,我们在做前后端分离项目的时候,经常需要在本地起前端工程,接口希望拉取服务端的实际数据而不是本地的mock数据,而如果本地程序直接访问远程接口, 肯定会遇到跨域问题。 什么是跨域?实现跨域的多种方式? ...

Global site tag (gtag.js) - Google Analytics