前端遇到的跨域问题及解决方案二

解决不同域之间JS交互问题,有这么几种方法。

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在浏览器下有长度限制,这个制约了每次传送的数据量。

举例子:

Cnblog.com下面的页面 parent.html 需要内嵌sina.com.cn下面的页面 child.html,要求二者可以通信,比如parent.html问Name,child回答Fany或者child主动提供页面的高度给parent

大致思路代码如下:

parent.html

<iframe id="iframeChild" src="…./child.html" width="100%" border="none"></iframe>

<script type="text/javascript">
var iChild=document.getElementById("iframeChild");
iChild.src="iframe-child.html#Height";//iframe-child.html#Name
function checkHash() {
  var data = location.hash ? location.hash.substring(1) : '';
  if (data) {
    //处理返回值
    console.log(data);
    //iChild.style.height=data;
    location.hash='';
    clearInterval(time1);
  }
}
var time1=setInterval(checkHash, 2000);

child.html

var Height=document.body.scrollHeight;
function checkHash(){
       var data = '',tHash=location.hash ? location.hash.substring(1) : '';
  if(tHash){
     switch(tHash){
           case 'Name': data = "Fany";clearInterval(time1);break;
           case 'Height': data = Height;clearInterval(time1);break;
           default:break;
      }
      window.parent.location.hash=data;
  } else return;
}
var time1=setInterval(checkHash, 2000);

如果也牵扯到前进、后退历史按钮功能的话,这个方法有点废!

Cross Frame

早之前提到这个方法的是这篇文章http://softwareas.com/cross-domain-communication-with-iframes当然,阅读量非常之高,所以再HTML5出来了postMessage之后,作者又更新了这篇文章。

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来代替。

现在假设 A..html 被iframe的页面是B.html B.html要向A.html传递自己的真实高度,会临时内嵌一个proxyA.html,proxyA.html与A..html传话之后,删除。

A..html

 <iframe id="b_iframe" height="0" width="0" src="http://www.sina.com.cn/b.html" border="none"></iframe>

B.html

var b_width = Math.max(document.documentElement.clientWidth,document.body.clientWidth);
var b_height = Math.max(document.documentElement.clientHeight,document.body.clientHeight);
var proxy = document.createElement('iframe');
  proxy.style.display = 'none';
  proxy.src = 'http://www.a.com/proxyA.html'+hash;
  document.body.appendChild(proxy);
var c_iframe = document.getElementById("proxyA");
c_iframe.src = c_iframe.src+"#"+b_width+"&"+b_height;
}

proxyA.html

var b_iframe = parent.parent.document.getElementById("b_iframe");
var hash_url = window.location.hash.split("#")[1].split("&");
var hash_width = hash_url[0]+"px";
var hash_height = hash_url[1]+"px";
b_iframe.style.width = hash_width;
b_iframe.style.height = hash_height;

postMessage(HTML5

html5中有个很酷的功能,就是跨文档消息传输(Cross Document Messaging)。新一代浏览器都将支持这个功能:Chrome 2.0+、Internet Explorer 8.0+, Firefox 3.0+, Opera 9.6+, 和 Safari 4.0+ 。

使用方法如下:

otherWindow.postMessage(message, targetOrigin);

说明:

otherWindow: 对接收信息页面的window的引用。可以是页面中iframe的contentWindow属性,window.open的返回值等。

message: 所要发送的数据,string类型。

targetOrigin: 用于限制otherWindow,“*”表示不作限制

www.a.com/A .html中的代码:

<iframe id="ifr" src="http://www.b.com/b.html"></iframe>

window.onload = function() {
    var ifr = document.getElementById('ifr');
    // 若写成'http://www.c.com'就不会执行postMessage了
    var targetOrigin = 'http://www.b.com';
    ifr.contentWindow.postMessage('sayHello', targetOrigin);
};

B.html中的代码

window.addEventListener('message', function(e){
  // 通过origin属性判断消息来源地址
  if (e.origin == 'http://www.a.com' &&
    e.data=='sayHello') {
    alert('Hello World');
  }
}, false);

库支持现在可以了。以下所有提供的一个接口的postMessage它是可用的,但如果它不是,回落到本文中描述的原始的技术:

Porthole

XSSInterface

EasyXDM

jQuery PostMessage Plugin

 document.domain+iframe

对于相同主域,子域不同的页面交互信息,可以通过设置document.domain的办法来解决。

比如shenghuo.alipay.com/A.html和personal.alipay.com/B.html设置document.domain = ‘alipay.com’。

A.html

document.domain='alipay.com';
var ifr = document.createElement('iframe');
ifr.src = 'personal.alipay.co/B.htm';
ifr.style.display = 'none';
document.body.appendChild(ifr);
ifr.onload = function(){
  //获取iframe的document对象W3C的标准方法是iframe.contentDocument,
  //IE6、7可以使用document.frames[ID].document
  //为了更好兼容,可先获取iframe的window对象iframe.contentWindow
  var doc = ifr.contentDocument || ifr.contentWindow.document;
  // 在这里操纵b.html
  alert(doc.getElementById("test").innerHTML);
};

B.html

<!DOCTYPE thml>
<html>
<head>
<title></title>
<script type="text/javascript">
  document.domain='alipay.com';
</script>
</head>
<body>
<h1 id="test">Hello World</h1>
</body>
</html>

如果b.html要访问a.html,可在子窗口(iframe)中通过window.parent来访问父窗口的window对象,然后就可以为所欲为了(window对象都有了,还有啥不行的),同理子窗口也可以和子窗口之间通信。

于是,我们可以通过b.html的XMLHttpRequest来获取数据,再传给a.html,从而解决跨子域获取数据的问题。

 动态script标签(Dynamic Script Tag

 这种方法也叫“动态脚本注入”。这种技术克服了XMLHttpRequest的最大限制,也就是跨域请求数据。直接用JavaScript创建一个新的脚本标签,然后设置它的src属性为不同域的URL。

www.a.com/a.html中的script

通过动态标签注入的必须是可执行的JavaScript代码,因此无论是你的数据格式是啥(xml、json等),都必须封装在一个回调函数中。一个回调函数如下:

www.a.com/a.html中的script

var dynScript = document.createElement('script');
dynScript.src = 'http://www.b.com/b.js';
dynScript.setAttribute("type", "text/javascript");
document.getElementsByTagName('head')[0].appendChild(dynScript);
function dynCallback(data){
  //处理数据, 此处简单示意一下
  alert(data.content);
}

在这个例子中,www.b.com/b.js需要将数据封装在上面这个dynCallback函数中,如下:

dynCallback({"content": "Hello World"})你可以传递更为复杂的数据。

不过动态脚本注入还是存在不少问题的,下面我们拿它和XMLHttpRequest来对比一下:

 XmlHttpRequestDynamic Script Tag
跨浏览器兼容NoYes
跨域限制YesNo
接收HTTP状态YesNo (除了200)
支持Get、PostYesNo (GET only)
发送、接收HTTP头YesNo
接收XMLYesYes
接收JSONYesYes
支持同步、异步YesNo (只能异步)

 可以看出,动态脚本注入还是有不少限制,只能使用Get,不能像XHR一样判断Http状态等。

而且使用动态脚本注入的时候必须注意安全问题。因为JavaScript没有任何权限与访问控制的概念,通过动态脚本注入的代码可以完全控制整个页面,所以引入外部来源的代码必须多加小心。

PS:文章有错误之处还请同仁指正,发现错误才会提高!参考文献如果有遗漏的,欢迎邮我哦^^!

 参考列表:

Two Methods for Handling Cross-Domain Ajax Calls

Window.postMessage

使用 window.name 解决跨域问题

Using window.name as a local data cache in web browsers

谈谈我对 window.name 实现跨域的理解

JS之Window对象

WEB跨域问题及解决方案

CROSS-DOMAIN COMMUNICATION WITH IFRAMES

如何解决js跨域问题

如何解决Ajax跨域问题-1

解决Ajax跨域问题

 

转载于:https://www.cnblogs.com/yixiaoheng/p/cross-domain-solution-2.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值