js解决跨域的三种方法,js跨域的几种方法

  js解决跨域的三种方法,js跨域的几种方法

  JavaScript跨域问题分析总结_直来直往_百度空间

  JavaScript跨域问题分析与总结2009-11-15 16:44一、为什么需要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进行登录验证。所以我们需要在每个子域下有一个服务器代理。好在这些子域都是我们自己的,对服务器上的程序有控制权,操作起来是可行的。但是,随着业务的不断发展,我们的合作伙伴越来越多。现在如果要和他们实现一些数据的跨域互通,需要和他们约定很多接口,然后一个一个实现代理,这样会带来很大的通信和维护成本。有没有更好的办法?是啊!那就是客户端的JS跨域解决方案。

  二、浏览器端的JS跨域方案

  无论是子域还是非子域,JAVASCRIPT本身都没有官方的跨域实现。以下跨域方案是开发人员创造的巧妙的黑客实现:

  1.跨子域设置document.domain。

  2.使用iframe的散列

  3.使用脚本标签

  4.闪存转发

  上述方案的应用场景和开发成本也有所不同。可以根据具体情况选择合适的跨域方案。

  1.设置document.domain

  如果只想跨子域,那么最简单的方法就是设置document.domain,配合隐藏的iframe,然后按照下面的方式完成异步请求。

  1.1使用表格提交

  当早期的浏览器不支持XMLHTTPRequest时,开发人员已经使用隐藏iframes和form的方式来提交来自同一来源的异步数据。原理是iframes相互通信。虽然我们现在做的是子域间的异步数据提交,但是我们通过设置domain获得了子域iframes间的控制权(需要注意的是,我们没有直接使用XMLHTTPRequest向auth子域下的页面发送异步请求的权限),所以下面要做的工作和以前没什么区别。以下是大致的描述。更具体地说,我们可以搜索使用iframes进行异步提交的资源。

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

  当用户在index.html输入登录信息并提交时,我们可以获得用户输入的用户名和密码,然后我们将这些信息写入proxy.html页面。通常我们会在代理页面中放置一个表单,然后在提交这个表单之前把要提交的数据写入这个表单,这样就完成了从www到auth的数据提交。那我们怎么知道用户是否成功登录了呢?我们需要一个反馈。proxy中表单对应的动作页面收到后会验证登录信息,然后以JS脚本的形式输出反馈信息,比如document . domain= XXX . com ;top.showLogin({code:0 , user : yoyo });这样,如果我们在索引页面中定义了showLogin函数,那么在用户登录后会自动调用,并且会收到auth的登录反馈。

  1.2使用XMLHTTPRequest提交到武汉百度推广。

  刚才,我们通过代理页面中的表单提交了数据。事实上,主流浏览器现在已经支持XMLHTTPRequest。我们可以完全控制代理页面实例化一个XMLHTTPRequest对象,所以这个实例在代理域下,它可以向auth.xxx.com提交AJAX请求。与我们页面本身发送的AJAX请求不同的是操作的窗口对象不同。如果我们把这种差异封装起来,用户就感觉不到差异了。

  2.使用iframe的hash和刚才一样。我们在A页(http://www.xxx.com/index.html)的auth子域下以iframe的形式插入一个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的设置操作,那么B如何在登录验证后反馈给A呢?同样,我们可以将一个www的A_proxy页面(http://www.xxx.com/proxy.html)以iframe的形式引入到B页面中,那么它也可以通过设置iframe的src来实现从B到A_proxy的设置操作,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的设定要求,只有B到A的设定要求)。需要注意的是,这种方式的缺点是在做SET操作时,受到各浏览器url长度的限制,无法发送太多数据。

  3.使用脚本标记武汉百度公司。

  当我们使用脚本标签的src属性将JS文件引入页面时,我们需要明确以下两条规则:

  3.1.文件下载后,JS会自动运行。

  3.2.不管src源的域是什么,这个JS执行时信任的域就是这个页面的域。

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

  这种方法的经典应用是JSONP,它在这个核心函数的基础上定义了一些规则:比如返回值都是JSON格式,在脚本标签的src中引入一个回调参数来指定回调函数名。比如flickr API,http://api.flickr.com/services/feeds/photos_public.gne?标签=猫标签模式=anyformat=JSON JSON回调=做点什么,返回的结果是doSomething({JSON format})。任何网站都可以通过JSONP调用这个API来显示这个照片列表。

  Jquery中还有getScript和getJSON的包,隐藏了脚本标签和回调函数的动态创建、垃圾回收、非跨域情况下的浏览器阻塞(具体细节的实现也很有意思,有时间我会单独写一篇分析)。用户只需要处理数据和回调函数。

  这种方法并不局限于跨子域的要求,但由于也是依赖于设置脚本标签的src属性的set操作,所以也受到各浏览器url长度的限制,不能发送太多数据。

  4.闪转发到武汉百度推广

  ACTIONSCRIPT本身也有发送异步请求的实现。虽然FlashPlayer也有安全域限制,但看起来比JS更人性化。刚从as转到JS的时候很惊讶。它也是基于ECMAScript的实现。为什么JS没有官方的跨域机制?首先,简单谈谈我们将在FlashPlayer的沙盒机制中使用的三个主要原则:

  4.1.FlashPlayer在发送请求时也需要遵循浏览器的同源策略。在早期版本中,子域被视为安全域。但随着安全级别的提高,JS目前遵循的同系策略只能向自己的同系(同协议、同域名、同端口)发送请求。如果需要向第三方的域发送请求,需要第三方将一个crossdomain.xml的XML文件放到自己的服务器上。这个文件就像一个白名单,你可以设置你信任的第三方域的访问权限。

  4.2.A域的页面引入了B域的Flash,无法与页面中的JS通信。在插入Flash时,域A的页面需要将allowscriptaccess属性设置为域B(Flash所在的域)或always(打开所有域)。

  4.3.A域页面中的JS也无法访问B域的Flash,所以Flash需要通过脚本中的allowDomain将A域设置为其可信安全域。

  知道了以上三个原理,就可以解决A域页面和B域flash的相互通信问题,以及B域flash和A域服务器的通信问题。下面是闪转的实现原理:

  用户交互还是由js来完成。当我们需要跨域发送请求时,可以用JS把数据传到FLASH,然后FLASH会跨域请求服务器,再把服务器处理的结果传到JS。实现的主要是网络通信和回调函数。需要特别注意的是,FLASH与JS通信时,如果字符串中出现,则需要在AS内对进行转义,否则在JS会被解析为转义字符。

  如果你是服务器端程序员,你可能已经有一个疑惑:真正的请求是b域的flash发出的,如果我们的请求需要A域的cookie信息怎么办?这里有必要提一下flashPlayer的一个很棒的‘特性’。无论flash的域在哪里,只要域A的服务器允许这个域的flash访问,当它向域A发送请求时,此时就会在浏览器中发送属于域A的cookie。所以,理论上你不需要担心这个问题,但是我需要提醒你两点:

  第一,这个特性仅限于URLRequest类,而AS中的文件上传类没有这个特性。相反,它的执行让人觉得不完整。参见“小心SWFUpload的cookie Bug”。

  第二,在我的项目中,曾经遇到过URLRequest类也丢失cookie的情况,但是再也没有出现过。我当时也不能排除其他因素的影响,所以只是加了‘理论’两个字。如果你遇到这种情况,也可以给我一个反馈,互相交流。现在为了保险起见,我还是主动用JS获取cookie,然后把它们加入到flash的请求中。

  在脸书的APP平台中有一个Fb:local-proxy的实现。其实核心就是闪转。它的优点是跨域没有限制,SET和GET操作不受数据长度限制。其缺点是客户端需要依赖Flash,需要考虑客户端的复杂度。

  测试环境部署麻烦,空余时间有限,所以没有写具体的样例代码,还是很粗糙的。原谅我。细节可以通过电子邮件或即时消息交流。

郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。

留言与评论(共有 条评论)
   
验证码: