一、问题提出: 从应用A跳转到应用B,用户在应用B上操作完毕后,关闭页面,是否可以用程序自动刷新应用A窗口,以让用户观察操作效果。如支付宝充值,跳转到各银行界面进行充值,充值完毕后,支付宝页面相关自动刷新。(当然由于跨域问题,支付宝并没有这么做,而是弹出层让用户回来确认是否充值完毕)
二、问题分析: 应用A采用域名http://trade.alibaba.com ,应用B采用的域名 http://56.alibaba.com。属于相同主域下的不同子域。牵涉出跨域是否能操作其他文档
三、问题延伸: 了解跨域,我们先了解一下javascript的同源策略,同源策略阻止从一个源加载的文档或脚本获取或设置另一个源加载的文档的属性。如果它们的协议、端口(如果指明了的话)和主机名都相同。则他们属于同源。深入了解同源策略对我们解决问题有很大帮助。
四、实验目的:
1、证明同源策略在各大浏览器下的表现是否统一
2、不同加载方式对同源策略的是否有不同的影响(包括父窗口打开子窗口、父窗口iframe子窗口、ajax调用其他域下的文档加载)
3、探寻解决跨域问题的办法
五、实验准备:
1、根据实验的目的,我们把我们需要做的实验用列表的方式展现出来,方便得出结论。结论填入下表
1 | 类型 | 页面A | 页面B | 页面关系和浏览器表现 (N为出错,Y为正常) |
||||||||
open | iframe | ajax | ||||||||||
ie | ff | g | ie | ff | g | ie | ff | g | ||||
2 | 协议、端口、域均相同 | http://www.alibaba.com/ domain1/a.htm |
http://www.alibaba.com/ domain2/b.htm |
Y | Y | Y | Y | Y | Y | Y | Y | Y |
3 | 协议、端口、域均相同 | http://www.alibaba.com:8080/ domain1/a.htm |
http://www.alibaba.com:8080/ domain2/b.htm |
Y | Y | Y | Y | Y | Y | Y | Y | Y |
4 | 协议不同 | http://www.alibaba.com/ domain1/a.htm |
https://www.alibaba.com/ domain2/b.htm |
|||||||||
5 | 端口不同 | http://www.alibaba.com/ domain1/a.htm |
http://www.alibaba.com:8080/ domain2/b.htm |
N | N | N | N | N | N | N | N | N |
6 | 域不同 | http://www.alibaba.com/ domain1/a.htm |
http://www.alisoft.com/ domain2/b.htm |
N | N | N | N | N | N | N | N | N |
7 | 子域(主机名)不同 | http://56.alibaba.com/ domain1/a.htm |
http://trade.alibaba.com/ domain2/b.htm |
N | N | N | N | N | N | N | N | N |
8 | 子域(主机名)不同,子域为空 | http://alibaba.com/ domain1/a.htm |
http://56.alibaba.com/ domain2/b.htm |
N | N | N | N | N | N | N | N | N |
9 | 子域不同设置 document.domain ="alibaba.com" 属性后 |
http://56.alibaba.com/ domain1/a.htm |
http://trade.alibaba.com/ domain1/a.htm |
Y | Y | Y | Y | Y | Y | N | N | N |
10 | 域不同,IP相同 | http://56.alibaba.com/ domain1/a.htm |
http://127.0.0.1/ domain2/b.htm |
N | N | N | N | N | N | N | N | N |
11 | 本地打开 | http://56.alibaba.com/ domain2/b.htm |
N | N | N | N | N | N | N | N/Y | N |
通过上面的表格,能确定同源策略在各个浏览器的支持程度,并检验各种页面引入方式对同源策略的影响。
2、其中A页面和B页面关系三种情况如下图:
图2-1 window.open打开子窗口
其中A页面源码如下:
- <html>
- <head>
- <title>Page A</title>
- <script>
- alert("domain is:"+document.domain);
- var openerWindow=function(url){
- window.open(url);
- }
- </script>
- </head>
- <body>
- <h1 style="text-align:center;font-size:100px;">A</h1>
- <div style="text-align:center"><input type="text" id="mytext" value="I'm page A"/><br/>
- <button onclick="openerWindow('http://www.alibaba.com/domain2/b.htm')">打开子窗口</button><!--url根据跨域更改-->
- </div>
- </body>
- </html>
其中B页面源码如下:
- <html>
- <head>
- <title>Page B</title>
- <script>
- alert("domain is:"+document.domain);
- var refleshOpener=function(){
- window.opener.location.reload();
- }
- </script>
- </head>
- <body>
- <h1 style="text-align:center;font-size:100px;">B</h1>
- <div style="text-align:center"><input type="text" id="mytext" value="I'm page B"/><br/>
- <button onclick="refleshOpener()">刷新父窗口</button>
- </div>
- </body>
- </html>
图2-2 iframe引入子窗口
其中A页面源码如下:
- <html>
- <head>
- <title>Page A</title>
- <script>
- alert("domain is:"+document.domain);
- var getValue=function(){
- alert("子窗口中值为:"+window.frames['little_frame'].document.getElementById('mytext').value);
- }
- </script>
- </head>
- <body>
- <h1 style="text-align:center;font-size:100px;">A</h1>
- <div style="text-align:center">
- <iframe id="little_frame" name="little_frame" width="300" height="300" src="http://www.alibaba.com/domain2/b.htm"></iframe><br/>
- <input type="text" id="mytext" value="I'm page A"/><br/>
- <button onclick="getValue()">获取子窗口属性</button>
- </div>
- </body>
- </html>
其中B页面源码如下:
- <html>
- <head>
- <title>Page B</title>
- <script>
- alert("domain is:"+document.domain);
- var getValue=function(){
- alert("父窗口中值为:"+top.document.getElementById('mytext').value);
- }
- </script>
- </head>
- <body>
- <h1 style="text-align:center;font-size:100px;">B</h1>
- <div style="text-align:center"><input type="text" id="mytext" value="I'm page B"/><br/>
- <button onclick="getValue()">获取父窗口属性</button>
- </div>
- </body>
- </html>
图2-3 ajax的load方法载入B.htm的内容
其中A页面源码如下:
- <html>
- <head>
- <title>Page A</title>
- <script src="jquery-1.3.2.js"></script>
- <script>
- alert("domain is:"+document.domain);
- var loadB=function(){
- $('#loadContentDiv').load("http://www.alibaba.com/domain2/b_ajax.htm");
- }
- </script>
- </head>
- <body>
- <h1 style="text-align:center;font-size:100px;">A</h1>
- <div style="text-align:center">
- <input type="text" id="mytext" value="I'm page A"/><br/><br/>
- <div id="loadContentDiv">这里载入Page B</div>
- <button onclick="loadB()">Ajax载入Page B</button>
- </div>
- </body>
- </html>
其中B页面源码如下:
- <html>
- <head>
- <title>Page B</title>
- <script>
- </script>
- </head>
- <body>
- <h1 style="text-align:center;font-size:100px;">B</h1>
- </body>
- </html>
3、服务器和页面部署:
页面位置如下:
服务器采用tomcat,在tomcat安装目录下conf/Catalina/localhost/ 新建domain1.xml、domain2.xml
配置分别如下:
- <?xml version="1.0" encoding="UTF-8"?> <Context docBase="D:/experiment/domain1" path="domain1" reloadable="true"></Context>
- <?xml version="1.0" encoding="UTF-8"?> <Context docBase="D:/experiment/domain2"
- path="domain2" reloadable="true"></Context>
4、通过host绑定来模拟跨域请求。
通过修改C:/windows/system32/drivers/etc/hosts文件
如在测试第5组时的绑定为:
127.0.0.1 www.alibaba.com
127.0.0.1 www.alisoft.com
六、实验过程
实验过程有些枯燥,对于协议或端口不同的情况,需要在本地架设两套服务器。
其中需要值得说明的是子域的情况:
在验证http://alibaba.com/domain1/a.htm和http://56.alibaba.com/domain2/b.htm的跨域的跨域过程中,有点令人费解的地方。即,页面a.htm不设置document.domain的值,默认为alibaba.com,在b.htm中设置document.domain='alibaba.com'。按道理应该可以绕过跨域的问题,但是这个时候,发现跨域的问题任然存在。于是在a.html中写入 document.domain=document.domain之后,跨域问题就解决了。是不是空的子域跟主域浏览器还是认为有差别呢。
七、实验结论
通过实验,我们可以得出结论
1、主要浏览器对javascript的同源特性的支持良好,不存在有的浏览器不行的情况。
2、对于不同的加载关系,包括open、iframe、ajax方式,也都表现统一。但是ajax加载的时候,在设置document.domain过程中,由于js在载入进来的时候未执行,所以被load页面的document.domain没有生效,造成该方式不能解决ajax的子域跨域问题。但是能用去其他两种打开关系解决子域的跨域。
八、跨域解决办法探讨
1、子域的跨域问题,上面的实验已经解决了两中情况,但是ajax加载的情况,还是得不到解决
2、我们经常引用其他站点的js,比如YUI的库,可以直接在yahoo网站上引用进来,并可以在本页面得到执行。也就是说<script>标签提供跨域特性,那么我们在页面A写入<script src='http://www.domain2.com/getData.jsp'></script>,那么该段js会跨域加载domain2下getData.jsp输出的内容。但是需要保证该输出为js允许的格式,比如是json串。这也就是jsonp的原理了。主要用于ajax跨域请求。但是对于操作跨域打开的窗口和iframe包含的窗口中的内容时,该方法也是力不从心。
3、我们打开一个页面或者嵌入iframe,再或者写入一个连接,经常会用到锚点这个概念。比如<a href='http://www.alibaba.com/b.htm#section1'>第一章节</a>这个#号后面跟的便是锚点的位置,用于页面级的定位。在js里面可以通过hash读取改值。ok,在跨域打开页面或者iframe页面的时候,我们也可以通过该方式写入这个hash值,完成跨域页面间的通信。但是只能父窗口跟子窗口传值,如果子窗口还需要传给父窗口值,那么还需要在子窗口中嵌入iframe,该iframe跟最上面的父窗口同域。这样就可以完成跨域页面的双向通信了。但是相互操作页面dom还是不可行的。如果有页面间的通信机制,其实很多跨域的问题就已经得到解决了。
当然,跨域的解决途径不仅仅只有上面提到的这三种方式。或者你可以也可以想想什么标签的什么特性可以用于跨域呢。e