Eagle

解决跨域的 10 种方法

2018-06-11

单向跨域

一、 JSONP(JSON with Padding)

html中的script标签可以加载并执行其他域的JavaScript,于是我们可以通过script标记来动态加载其他域的资源

例子:
【域A】中【pageA】需要加载【域B】的数据,那么在【域B】的页面【pageB】中我以JavaScript的形式声明【pageA】需要的数据,然后在【pageA】中用script标签把【pageB】加载进来,那么【pageB】中的脚本就会得以执行。JSONP 在此基础上加入了【回调函数】,【pageB】加载完之后会执行【pageA】中定义的函数,所需要的数据会以【参数的形式】传递给该函数

优点:易于实现, 在受信任的双方传递数据,JSONP是非常合适的选择。
缺点:如果第三方的脚本随意地执行,那么它就可以篡改页面内容,截获敏感数据。

二、flash URLloader

flash有自己的一套安全策略,服务器可以通过crossdomain.xml文件来声明能被哪些域的SWF文件访问,SWF也可以通过API来确定自身能被哪些域的SWF加载。

例子:
从域 www.a.com 请求域 www.b.com 上的数据,我们可以借助 flash 来发送 HTTP 请求。

  1. 修改域 www.b.com 上的 crossdomain.xml (一般存放在根目录,如果没有需要手动创建) ,把 www.a.com 加入到白名单。
  2. 通过 Flash URLLoader 发送 HTTP 请求。
  3. 通过 Flash API 把响应结果传递给 JavaScript。

缺点:不支持 IOS

三、Access Control

Access Control 是比较超越的跨域方式,浏览器可以发送一个跨域的 HTTP 请求(Firefox, Google Chrome等通过 XMLHTTPRequest 实现,IE8 通过 XDomainRequest 实现),请求的响应必须包含一个 Access-Control-Allow-Origin 的 HTTP 响应头,该响应头声明了请求域的可访问权限。

例子:
www.a.com对www.b.com下的asset.php发送了一个跨域的HTTP请求,那么asset.php必须加入如下的响应头

缺点:目前只在很少的浏览器中得以支持,

四、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的方式来抓取数据。

例子:
www.a.com 域下的页面需要请求 www.b.com 下的资源文件 asset.txt 时,直接发送一个指向 www.b.com/asset.txt 的 Ajax 请求肯定是会被浏览器阻止。这时,我们在 www.a.com 下配一个代理,然后把 Ajax 请求绑定到这个代理路径下,例如 www.a.com/proxy/ , 然后这个代理发送 HTTP 请求访问 www.b.com 下的 asset.txt ,跨域的 HTTP 请求是在服务器端进行的,客户端并没有产生跨域的 Ajax 请求。

注意:这个跨域方式不需要和目标资源签订协议,带有侵略性,另外需要注意的是实践中应该对这个代理实施一定程度的保护,比如限制他人使用或者使用频率。

双向跨域

一、document.domain

通过修改document的domain属性,我们可以在域和子域或者不同的子域之间通信。

例子:
同域策略认为域和子域隶属于不同的域,比如 www.a.com 和 sub.a.com 是不同的域,这时,我们无法在 www.a.com 下的页面中调用 sub.a.com 中定义的 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 的改变,在可用性和性能上都做了很大的改观。它的基本原理大致是这样的,假设在域 www.a.com 上有页面 A.html 和一个空白代理页面 proxyA.html , 另一个域 www.b.com 上有个页面 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 来代替。

不同的跨域请求可以使用不同的方式解决

原文链接:http://www.cnblogs.com/hustskyking/articles/ten-methods-cross-domain.html

Tags: 跨域