主要思想
通过在应用页面设置一个iframe
,其源头指向拥有数据的文件或页面,让那个data(数据页面)把值赋在那个iframe的window.name
上,然后由于window.name
在浏览器环境中是一个全局/window对象的属性
,且当在 frame 中加载新页面时,name 的属性值依旧保持不变。通过在 iframe 中加载一个资源,该目标页面将设置 frame 的 name 属性。此 name 属性值可被获取到,以访问 Web 服务发送的信息。但 name 属性仅对相同域名的 frame 可访问。这意味着为了访问 name 属性,当远程 Web 服务页面被加载后,必须导航 frame 回到原始域。(就是说先用data把数据放在iframe的window.name里面,然后切换到proxy ,这时proxy拿到window.name的数据,就可以向同域的app传值了)同源策略依旧防止其他 frame 访问 name 属性。一旦 name 属性获得,销毁 frame 。
具体实现
有三个页面:
- a.com/app.html:应用页面。
- a.com/proxy.html:代理文件,一般是一个没有任何内容的html文件,需要和应用页面在同一域下。
- b.com/data.html:应用页面需要获取数据的页面,可称为数据页面。
实现起来基本步骤如下:
1、在应用页面(a.com/app.html)中创建一个iframe,把其src指向数据页面(b.com/data.html)。
数据页面会把数据附加到这个iframe的window.name上,data.html代码如下:
1 | <script type="text/javascript"> |
2、在应用页面(a.com/app.html)中监听iframe的onload事件,在此事件中设置这个iframe的src指向本地域的代理文件(代理文件和应用页面在同一域下,所以可以相互通信)。app.html部分代码如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20<script type="text/javascript">
var state = 0,
iframe = document.createElement('iframe'),
loadfn = function() {
if (state === 1) {
var data = iframe.contentWindow.name; // 读取数据
alert(data); //弹出'I was there!'
} else if (state === 0) {
state = 1;
iframe.contentWindow.location = "http://a.com/proxy.html"; // 设置的代理文件
}
};
iframe.src = 'http://b.com/data.html';
if (iframe.attachEvent) {
iframe.attachEvent('onload', loadfn);
} else {
iframe.onload = loadfn;
}
document.body.appendChild(iframe);
</script>
3、获取数据以后销毁这个iframe,释放内存;这也保证了安全(不被其他域frame js访问)。
1 | <script type="text/javascript"> |
总结
iframe的src属性由外域转向本地域,跨域数据即由iframe的window.name从外域传递到本地域。这个就巧妙地绕过了浏览器的跨域访问限制,但同时它又是安全操作。