`
xiaoboss
  • 浏览: 651674 次
  • 性别: Icon_minigender_1
  • 来自: 广州
社区版块
存档分类
最新评论

JavaScript跨域问题分析与总结

 
阅读更多

一、为什么需要JS跨域
假设我们构建了一个网上商城www.xxx.com,出于对用户账号安全性的考虑,我们将用户登录统一到auth.xxx.com的子域下验证。当一个未登录用户浏览商品以后点击购买,为了提高网站的用户体验,我们想提供一个无刷新的登录入口。我们立刻想到使用AJAX实现无刷新的数据交互,可当我们实际使用AJAX向auth.xxx.com提交数据的时候,JS却出现错误提示,我们没有权限进行此操作,因为XMLHTTPRequest的实现要遵循浏览器的安全模型的同源策略规则,JS只能往自己的同源(同协议、同域名、同端口)发送XMLHTTPRequest请求,所以我们无法往子域发送AJAX请求。

解决此问题的通常做法是由auth.xxx.com提供接口,在www.xxx.com的域下做一个服务端代理。但随着业务的发展,我们又有a.xxx.com,b.xxx.com...等等都需要到auth.xxx.com做登录验证,那么我们需要在每一个子域下都有一个服务端的Proxy,值得庆幸的是这些子域都是我们自己的,对服务器上的程序有控制权,所以操作起来还是可行的。然而随着业务的继续发展,我们有越来越多的合作伙伴,现在我们要与他们实现部分数据的跨域互通,那么我们就需要和他们去约定一大堆的接口,然后自己逐一去实现Proxy,那么这种方式会带来很大的沟通和维护成本。有没有更好的方式呢?有!那就是客户端的JS跨域方案。

二、浏览器端的JS跨域方案
无论是子域还是非子域,JAVASCRIPT本身并没有官方的跨域实现,下面介绍的跨域方案都是开发者创造出来的巧妙的hack实现:
1、设置document.domain跨子域
2、使用iframe的hash
3、使用script标签
4、flash转发

上面各种方案的适用场景和开发成本也是不同的,你可以根据具体的情况来选择合适的跨域方案。


1、设置document.domain
如果仅仅是要跨子域,那么最简单的方式是设置document.domain,再配合隐藏的iframe,然后通过下面的方式来完成异步请求。
1.1 使用FORM提交
早期浏览器并不支持XMLHTTPRequest时,开发者就已经使用隐藏iframe和form的方式来做同源的异步数据提交,原理就是iframe间的相互通信。虽然我们现在是在子域间做异步数据提交,但通过设置domain我们已经获取了子域iframe之间的控制权(需要注意的是并没有直接使用XMLHTTPRequest往auth子域下的页面发送异步请求的权限),那么下面要做的工作和以前没有区别,这里就大概描述一下,更具体的可以搜索下使用iframe做异步提交的资源。

我们在A页面(http://www.xxx.com/index.html)中以iframe形式插入一个auth子域下的B页面(http://auth.xxx.com/proxy.html),并设置index.html和proxy.html页面的domain为xxx.com,这样index.html和proxy.html就有了互相访问的权限。

当用户在index.html输入登录信息提交时,我们可以获取到用户输入的用户名和密码,然后我们把此信息写入到proxy.html页面中。通常我们会在proxy页面中放置一个form,然后把需要提交的数据写入到此form中再提交此form,这样就完成了www到auth的数据提交。那我们怎么知道用户是否登录成功呢,我们需要一个反馈。proxy中的form对应的action页面接受到登录信息以后做验证,然后将反馈信息以JS脚本的形式输出,例如document.domain='xxx.com';top.showLogin({'code':'0','user':'yoyo'});,这样如果我们在index页面中定义了showLogin函数,那么用户登录以后就会自动调用,并且接收都到了auth的登录反馈。

1.2 使用XMLHTTPRequest提交

刚才我们是把数据通过proxy页面中的FORM提交出去的,实际上现在的主流浏览器都已经支持XMLHTTPRequest,我们完全可以控制proxy页面实例化一个XMLHTTPRequest对象,那么这个实例是在proxy的域下的,它是可以往auth.xxx.com提交AJAX请求的。这和我们页面本身发送AJAX请求所不同的仅仅在于操作的window对象不同,如果我们把这个差异封装起来,那使用者也是感受不到其中的差异的。

2、使用iframe的hash 同刚才的做法我们在A页面(http://www.xxx.com/index.html)中以iframe形式插入一个auth子域下的B页面(http://auth.xxx.com/proxy.html),但这次我们不需要设置document.domain,受安全策略的限制A页面的JS无法获取到A页面中iframe的src属性,更没有办法访问页面B中的元素,但它可以设置iframe的src,假如A页面输入的用户名和密码同时以一定格式加入到了src对应的url中,例如:http://auth.xxx.com/proxy.html?user=yoyo&pwd=123456,B页面是可以在GET信息中获取此登录信息做验证的。这就完成了A到B的SET操作。那B登录验证以后怎么给A反馈呢?同理,我们可以在B页面以iframe形式引入一个www下的A_proxy页面(http://www.xxx.com/proxy.html),那么它也可以通过设置iframe的src的方式实现B到A_proxy的SET操作,而A_proxy和A是一个域,他们之间就可以自由地完成通信了。

如果我们把参数以GET的形式设置到iframe的src中,会造成iframe频繁发送请求。为了避免这点,我们可以把参数以锚点的形式传入进入,同时在iframe里对锚点的改变做一个监听即可。那如何监听锚点的改变呢?
2.1:可以在iframe中使用setInterval频繁检查location.hash是否改变。如果你对你的代码效率有洁癖,那么你可以考虑使用下面的方法。
2.2:在设置iframe的src的同时,改变一下iframe的width属性,iframe内部使用onresize事件获知hash的改变。

此方法不局限于跨子域的需求,在FaceBook的APP平台中,第三方应用的iframe的高度自适应就是采用此方法实现的(实际上iframe高度自适应没有A到B的set需求,仅仅是B到A的set需求)。需要注意的是此方法的弊端在于做SET操作的时候受制于各个浏览器对url长度的限制,无法发送过多的数据。

3、使用script标签
当我们以script标签的src属性引入一个JS文件到页面中时,我们需要清楚下面两条规则:
3.1、文件下载完毕以后,其中的JS会自动运行。
3.2、无论src来源的域是什么,这段JS执行时信任的域都是此页面的域。

当用户登录的时候,A页面使用JS动态添加一个script节点到页面中,并且把用户名和密码设置到src属性中,例如:http://auth.xxx.com/login?user=yoyo&password=123456,那么完成了A到B的SET操作,当B做完验证以后,把结果以JS形式返回过来,例如:showLogin({'code':'0','user':'yoyo'})。根据规则一这个JS函数会自动执行,如果我们在页面A中定义了showLogin的函数,这就实现了B到A的回调反馈。

此方法的经典应用就是JSONP,它在此核心功能的基础上有定义了一些规则:例如返回值都是JSON格式,script标签的src中引入一个callback参数指定回调函数名。例如flickr的API,http://api.flickr.com/services/feeds/photos_public.gne?tags=cat&tagmode=any&format=json&jsoncallback=doSomething,返回的结果就是doSomething({JSON格式}),任何网站都可以通过JSONP调用此API来展示此照片列表。

在Jquery中也有getScript和getJSON的封装,它隐藏了script标签和回调函数的动态创建、垃圾回收、非跨域情况下的浏览器阻塞(具体细节的实现也是很有意思的,有空我会单独写一个分析),使用者只需要处理数据和回调函数即可。

此方法不局限于跨子域的需求,但由于它也是依赖设置script标签的src属性来做的set操作,所以它同样受制于各个浏览器对url长度的限制,无法发送过多的数据。

4、flash转发

ACTIONSCRIPT本身也有发送异步请求的实现,虽然FlashPlayer也有安全域限制,但相对于JS而言似乎更人性化。最初我由AS转入JS的时候就很诧异,同样是基于ECMAScript的实现,为何JS没有官方的跨域机制呢?首先简单讲一下FlashPlayer沙箱机制中的我们会用到的三个主要原则:

4.1、FlashPlayer发送请求的时候也需要遵循浏览器的同源策略,在早期版本中子域被视为安全域,但随着安全级别的提高,目前遵循的同源策略和JS一样,只能往自己的同源(同协议、同域名、同端口)发送请求。如果需要往第三方的域发送请求,那么需要第三方在自己的server上放置一个crossdomain.xml的XML文件。此文件就好比是一个白名单,可以设置自己信任的第三方域的访问。

4.2、A域的页面引入了一个B域的Flash,此Flash是无法和页面中的JS通信的,需要A域页面在插入Flash的时候设置allowscriptaccess属性为B域(flash所在的域),或者是always(开放所有域)。

4.3、A域的页面中的JS也无法访问B域的Flash,需要Flash在脚本中通过allowDomain设置了A域为自己信任的安全域。

了解了上面三个原则以后,我们就可以解决A域页面和B域的flash的互相通信的问题,B域flash和A域的server通信的问题。下面介绍下Flash转发的实现原理:

用户交互还是由JS完成,当我们需要跨域发送请求的时候,我们可以使用JS把数据传递给FLASH,然后由FLASH去跨域请求server,然后把server处理完毕的结果传递给js。具体实现主要是网络通信和回调函数,需要特别注意的是FLASH和JS通信的时候,字符串中如果出现"\",那么需要在AS内部对"\"做一次转义,否则到了JS端此"\"会被当作转义符解析掉。

如果你是一个server端的程序员,你可能已经有一个疑惑了:真实的请求是由B域的flash发送的,如果我们的这个请求需要A域下的cookie信息怎么办呢?这里就需要提到一个FlashPlayer的奇妙"特性",无论flash的域在哪儿,只要A域的server允许此域的flash访问,它向A域发送请求的时候,都会把此时浏览器中属于A域的cookie一同发送过去,因此"理论"上你不需要担心这个问题,但我需要提醒两点:

一、这个特性仅限于URLRequest类,AS中的文件上传类就没有此特性,相反它的实现让人感觉是残缺的,见"小心SWFUpload的cookie Bug"。

二、在我项目中我曾经遇到过URLRequest类也丢失cookie的情况,但后来一直没有再重现,我也无法排除当时有其他因素的影响,所以我刚才加了"理论"二字。如果你遇到了这个情况,也可以给我一个反馈,一起交流一下。现在我为了保险起见,我还是主动用JS把cookie获取,然后加入到flash的请求中。

在Facebook的APP平台中有一个Fb:local-proxy的实现,实际上核心就是Flash转发,它的优势在于跨域没有局限,SET和GET操作也没有数据长度的局限,劣势在于客户端需要依赖FLASH,需要考虑的客户端复杂性。

测试环境部署麻烦,而且空余时间也有限,所以没有去写具体的示例代码,上面写得还是很粗,见谅。细节问题可以邮件、IM交流。

分享到:
评论

相关推荐

    JavaScript 跨域通信方法

    ### JavaScript父子页面跨域通信详解 #### 一、引言 在现代Web开发中,跨域通信是一个常见的问题。由于浏览器的安全策略——同源策略(Same-origin policy)的存在,JavaScript在处理不同源之间的数据交互时会受到...

    JSP使用ajaxFileUpload.js实现跨域问题.docx

    跨域问题是由于浏览器的同源策略导致的,该策略限制了一个域下的JavaScript脚本获取另一个不同源(协议、域名或端口)的数据。为了解决这个问题,很多前端框架提供了相应的解决方案,如jQuery的`$.ajax`方法支持...

    JavaScript必看全面总结.zip

    13. **跨域问题及解决方案**:理解同源策略,以及如何通过JSONP、CORS、代理服务器等方式解决跨域问题。 14. **Node.js基础**:了解JavaScript在服务器端的应用,Node.js的事件驱动模型,以及文件系统、网络请求等...

    js关于getImageData跨域问题的解决方法

    总结而言,当我们在JavaScript中遇到关于canvas的getImageData方法的跨域错误时,我们应该优先考虑将资源部署到服务器上以遵循同源策略。这是保证Web应用安全、兼容和标准化的最有效方法。同时,开发者应当充分理解...

    Jquery跨域Json请求处理

    跨域问题是前端开发中不可避免的一个挑战,通过合理运用JSONP等技术,可以在不违反浏览器安全策略的情况下,实现跨域数据的高效获取与处理。需要注意的是,在实际应用中应考虑多种跨域解决方案,并结合项目需求选择...

    vue实现跨域的方法分析.docx

    ### Vue实现跨域的方法分析 #### 一、背景介绍 在现代Web开发中,跨域问题是一个常见的技术挑战。浏览器为了安全考虑,默认情况下不允许不同源(即协议、域名或端口不同的网页)之间的数据交互。这给前端开发带来...

    js跨域访问后台

    在实际开发中,由于前后端分离的趋势越来越明显,前端页面可能托管在一个服务器上,而后端服务则部署在另一个服务器上,这就需要解决跨域问题。跨域能力使得前端可以调用不同服务器上的API接口,从而实现业务功能。 ...

    Ext.Ajax.request跨域

    打开并分析这个文件可以帮助我们更好地理解实际项目中跨域的实现方式。 5. 源码与工具: 标签"源码 工具"提示这个话题可能涉及到具体的代码实现和一些辅助工具。在实际开发中,开发者可能需要用到如Postman这样的...

    高度自适应 跨域访问实例下载

    1. **跨域访问**:在Web浏览器的安全策略下,不同源的HTTP请求(如JavaScript的XMLHttpRequest或AJAX)受到同源策略的限制,即只能请求与当前页面同协议、同域名、同端口的资源。跨域访问就是打破这个限制,允许不...

    建立谷歌分析与网站优化工具跨域跟踪.pdf

    在互联网营销和数据分析中,确保跨域跟踪的准确性和完整性是至关重要的,特别是当你运营的网站涉及多个子域或完全不同的域名时。谷歌分析(Google Analytics)和谷歌优化工具(Website Optimizer)提供了这样的功能...

    跨域请求的几种方式

    2. **跨域问题**:当一个页面试图加载或访问另一个来源的资源时,如果该资源所在的域名与页面自身的域名不同,则会触发跨域问题。这里的“来源”定义为由协议、主机名和端口号组成的组合。 #### 三、解决跨域问题的...

    AJAX通过跨域传输信息生成PDF文件

    总结起来,这个示例展示了如何结合AJAX、JSONP和TCPDF库,实现跨域数据传输并生成PDF文件。这种技术常用于动态报告、数据分析和自定义打印场景,使得Web应用能够提供更丰富的交互性和功能性。了解并掌握这些技术,...

    ajax解决跨域的全面方案.docx

    总结,解决Ajax跨域问题需要理解同源策略的基本原理,并根据实际需求选择合适的解决方案,如JSONP、CORS或代理。在实际开发中,通常推荐使用CORS,因为它提供了更强大的控制和安全性。同时,了解其表现和解决方法,...

    jsonp跨域获取百度联想词的方法分析.docx

    JSONP是一种解决JavaScript跨域问题的有效方法,尤其适用于简单的GET请求。但需要注意的是,它不支持POST等其他HTTP方法,也无法处理状态码和头部信息,而且只适用于支持动态注入脚本的场景。在实际开发中,考虑到...

    ajax跨域访问

    ### AJAX跨域访问详解 #### 一、引言 在Web开发中,由于浏览器的安全策略限制...通过以上分析,我们可以看到,虽然JSONP在解决跨域问题上具有一定的优势,但在实际应用中还需要综合考虑多种因素来选择最合适的方案。

    jQuery 获取跨域XML(RSS)数据的相关总结分析

    标题和描述中提到了“jQuery获取跨域XML(RSS)数据的相关总结分析”,这涉及到了前端JavaScript开发中一个常见的问题——同源策略的限制。同源策略是浏览器的一个安全功能,它要求网页中的脚本只能访问与该网页同源的...

    XSS跨域攻击讲义

    ### XSS跨域攻击知识点详解 #### 一、XSS攻击概览 - **定义与起源**:跨站脚本(Cross-Site Scripting,简称XSS)是一种常见的Web应用程序安全漏洞,最早出现于1996年。XSS攻击利用的是网站对用户输入数据过滤不严...

    jsonp跨域请求数据实现手机号码查询实例分析.docx

    ### JSONP 跨域请求数据实现手机号码查询实例分析 #### 前言 在现代Web开发中,跨域问题一直是前端开发者面临的一个常见难题。简单来说,由于浏览器的同源策略限制,当一个网页尝试从不同的源(即不同的协议、端口...

    getJSON跨域SyntaxError问题分析

    根据提供的文件内容,我们可以探讨getJSON跨域请求...总结来说,解决getJSON跨域请求遇到的SyntaxError问题,关键在于服务器端需要正确地设置CORS相关的HTTP响应头,并且根据需要实现JSONP功能,以绕过同源策略的限制。

Global site tag (gtag.js) - Google Analytics