常用跨域方法实践(二)

本文承接 上一篇博文,上一篇博文介绍了跨域的相关概念、测试demo的相关配置和 JSONP 和 CORS 两种跨域方式的实现。本文主要介绍 document.domain 、 URL.hash 、 cross-fragment 、 window.name 和 postMessage 这五种方式的跨域实现。

document.domain

如果 源和 源具有相同的父域名,通过设置 document.domain 属性,就很容易使其相互通信。在 HTML 规范中document.domain 是一个只读属性,现代浏览器允许将其设置为父域名(不是顶级域名)。例如,一个是 www.myapp.com 的页面,可以设置为 myapp.com ,而另一个来自 sample.myapp.com 的页面也可以设置为 myapp.com ,下图展示了document.domain 的工作原理:

通过将不同子域名的 document.domain 属性设置为相同的父域名,来实现不同子域名之间的跨域通信,这并不属于同源策略限制的范畴。但是,严格来说,子域名跨域的解决方案最适用于内部应用之间的跨域通信。

访问页面:document_domain_test.ejs

  <button>测试</button>
  <div></div>
  <iframe id="ifr" src="http://sample.myapp.com/document_iframe"></iframe>

  <script>
      document.domain = "myapp.com";
      function sayHello(str) {
        document.querySelector(‘div‘).innerHTML = str;
      }
      document.querySelector(‘button‘).onclick = function() {
        document.querySelector(‘#ifr‘).contentWindow.sayHello(‘Hello son‘);
      }
  </script>

跨域iframe:document_iframe.ejs

    document.domain = ‘myapp.com‘;
    function sayHello(str) {
        document.querySelector(‘div‘).innerHTML = str;
        parent.sayHello(‘Hello father‘);
    }

效果:

URL.hash

一个 URL 由下图所示的几个部分组成:

一般来说, URL 的任何改变都重新会加载一个新的网页,除了 hash 的变化, hash 的任何改变都不会导致页面刷新。 hash 已经被广泛使用在支持局部刷新的 SPA 应用中,用来记录用户的访问路径。在跨域解决方案中, hash 也非常有用,来自不同源的页面可以相互设置对方的 URL ,包括 hash 值,但仍被限制获取对方的 hash 值。文档之间可以通过 hash 来相互通信。如下图所示的例子:

访问页面:url_hash_test.ejs

  <button>发送消息</button>
  <iframe id="ifr" src="http://www.otherapp.com/hash_iframe"></iframe>
  <script>
    var target = "http://www.otherapp.com/hash_iframe";
    function sendMsg(msg) {
      var data = {msg: msg};
      var src = target + "#" + JSON.stringify(data);
      document.getElementById(‘ifr‘).src = src;
    }
    document.querySelector(‘button‘).onclick = function() {
      sendMsg("Time: " + (new Date()));
    }
  </script>

跨域iframe:hash_iframe.ejs

  <div></div>
  <script>
    var oldHash = "";
    var target = "http://www.myapp.com/url_hash_test";
    var checkMessage = function() {
      var newHash = window.location.hash;
      if (newHash.length > 1) {
        newHash = newHash.slice(1, newHash.length);
        if (newHash !== oldHash) {
          oldHash = newHash;
          var msgs = JSON.parse(newHash);
          var msg = msgs.msg;
          sendMessage("Hello-father");
          document.querySelector(‘div‘).innerHTML = msg;
        }
      }
    }
    setInterval(checkMessage, 1000);
    function sendMessage(msg) {
      var hash = "msg=" + msg;
      parent.location.href = target + "#" + hash;
    }
  </script>

主页面通过改变 iframe 的 hash 值向 iframe 传递参数, iframe 不断获取自己的 hash 值,一旦发生变化就立即显示主页面传来的消息,并且通过设置主页面的 hash 就可以像主页面传递消息了,这样实际就可以完成双向的跨域通信了。

效果:

Cross-fragment

由于许多网站的 hash 已经被用于其他用途,对于这样的网站用 hash 跨域将非常复杂(需要从 hash 中合并和分离出消息)。因此我们就有了基于 hash 的一个升级版: cross-fragment ,其原理如下图所示:

访问页面:cross_fragment_test.ejs

  <button>发送消息</button>
  <div></div>
  <iframe id="otherapp" name="otherapp" src="http://www.otherapp.com/fragment_iframe"></iframe>

  <script>
    function sendMsg(msg) {
      var frame = document.createElement("iframe");
      var baseProxy = "http://www.otherapp.com/fragment_req_proxy";
      var request = {frameName: "otherapp", data: msg};
      frame.src = baseProxy + "#" + encodeURI(JSON.stringify(request));
      frame.style.display = "none";
      document.body.appendChild(frame);
    }
    // 响应处理函数
    function getResponse(data) {
      document.querySelector(‘div‘).innerHTML = "收到结果:" + data;
    }
    document.querySelector(‘button‘).onclick = function() {
      // 发送一个随机数
      sendMsg(Math.random());
    }
  </script>

请求代理页面:fragment_req_proxy.ejs

  <script>
    var hash = window.location.hash;
    if (hash && hash.length > 1) {
      var request = hash.slice(1, hash.length);
      var obj = JSON.parse(decodeURI(request));
      var data = obj.data;
      //目标页面的getData方法
      parent.frames[obj.frameName].getData(data);
    }
  </script>

跨域iframe:fragment_iframe.ejs

  <div></div>
  <script>
    function getData(data) {
      document.querySelector(‘div‘).innerHTML = "收到的消息:" + data;
      var frame = document.createElement("frame");
      var baseProxy = "http://www.myapp.com/fragment_res_proxy";

      // 将接收的数据扩大一百倍
      var request = {data: data * 100};
      frame.src = baseProxy + "#" + encodeURI(JSON.stringify(request));
      frame.style.display = "none";
      document.body.appendChild(frame);
    }
  </script>

响应代理页面:fragment_res_proxy.ejs

  <script>
    var hash = window.location.hash;
    if (hash && hash.length > 1) {
      var request = hash.slice(1, hash.length);
      var obj = JSON.parse(decodeURI(request));
      var data = obj.data;
      //主页面的getResponse方法
      parent.parent.getResponse(data);
    }
  </script>

这个例子中先在主页面(来自myapp)放置一个otherapp下的 iframe (目标页面),点击按钮后就在主页面中构造一个隐藏的iframe (和目标页面同域,来自otherapp,请求代理页面),通过这个请求代理页面调用目标页面的 getData 方法,这个方法接收传来的数据,处理完成后,构造一个隐藏的 iframe (和主页面同域,来自myapp,响应代理页面),通过响应代理页面调用主页面中的 getResponse 方法。

效果:

之前整理过一个利用该方法实现 iframe自适应的博文,有兴趣的可以看一下

window.name

window.name 跨域是一个巧妙的解决方案,我们一般使用 window.name 的情况如下:

  • 使用 window.frames[windowName] 得到一个子窗口
  • 将其设置为链接元素的 target 属性

加载任何页面 window.name 的值始终保持不变。由于 window.name 这个显著的特点,使其适用于在不同源之间进行跨域通信,但这是个不常用的属性。那么怎么在同源策略下使用呢?下图显示了如何使用 window.name 来跨域通信。

当页面 想要从另一个源获取资源或Web服务,首先在自己的页面上创建一个隐藏的 iframe B,将 B 指向外部资源或服务,B 加载完成之后,将把响应的数据附加到 window.name 上。由于现在 A 和 B 还不同源,A 依旧不能获取到 B 的 name 属性。当 B 获取到数据之后,再将页面导航到任何一个与 A 同源的页面,这时 A 就可以直接获取到 B 的 name 属性值。当 A 获取到数据之后,就可以随时删掉 B。

访问页面:window_name_test.ejs

  <button>发送消息</button>
  <div class="req"></div>
  <div class="res"></div>
  <script>
    function sendMsg(msg) {
      var state = 0, data;
      var frame = document.createElement("frame");
      var baseProxy = "http://www.otherapp.com/window_iframe";
      var request = {data: msg};
      frame.src = baseProxy + "#" + encodeURI(JSON.stringify(request));
      frame.style.display = "none";
      frame.onload = function() {
        if (state === 1) {
          data = frame.contentWindow.name;
          document.querySelector(‘.res‘).innerHTML = "获得响应:" + data;
          // 删除iframe
          frame.contentWindow.document.write(‘‘);
          frame.contentWindow.close();
          document.body.removeChild(frame);
        } else {
          state = 1;
          frame.src = "http://www.myapp.com/window_iframe";
        }
      };
      document.body.appendChild(frame);
    }
    document.querySelector(‘button‘).onclick = function() {
      var val = Math.random();
      sendMsg(val);
      document.querySelector(‘.req‘).innerHTML = "请求数据:" + val;
    }
  </script>

跨域iframe:window_iframe.ejs

  <script>
    var hash = window.location.hash;
    if (hash && hash.length > 1) {
      var request = hash.slice(1, hash.length);
      var obj = JSON.parse(decodeURI(request));
      var data = obj.data;
      window.name = data*100;
    }
  </script>

效果: 

postMessage

HTML5 规范中的新方法 window.postMessage(message, targetOrigin) 可以用于安全跨域通信。当该方法被调用时,将分发一个消息事件,如果窗口监听了相应的消息,窗口就可以获取到消息和消息来源。如下图所示:

如果 iframe 想要通知不同源的父窗口它已经加载完成,可以使用 window.postMessage 来发送消息。同时,它也将监听回馈消息。

访问页面:postMessage_test.ejs

  <div></div>
  <iframe name="otherApp" id="otherApp" src="http://www.otherapp.com/postMessage_iframe"></iframe>
  <script>
    function handleReceive(event) {
      if (event.origin != "http://www.otherapp.com") return;
      // 处理数据
      var data = JSON.parse(event.data);
      document.querySelector(‘div‘).innerHTML = "来自iframe的颜色:" + data.color;
      var otherAppFrame = window.frames["otherApp"];
      otherAppFrame.postMessage("ok", "http://www.otherapp.com");
    }
    window.addEventListener("message", handleReceive, false);
  </script>

跨域iframe:postMessage_iframe.ejs

  <div></div>
  <script>
    function postMessage(msg) {
      var targetWindow = parent.window;
      targetWindow.postMessage(msg, "*");
    }
    function handleReceive(msg) {
      if (msg.data === "ok") {
        document.querySelector(‘div‘).innerHTML = "消息发送成功";
      } else {
        postMessage(JSON.stringify({color: ‘red‘}));
      }
    }
    window.addEventListener("message", handleReceive, false);
    window.onload = function() {
      postMessage(JSON.stringify({color: ‘red‘}));
    }
  </script>

效果:

结语

本文介绍了常用的五种跨域实现方法,测试源码 请戳这

参考

时间: 2024-11-03 21:57:00

常用跨域方法实践(二)的相关文章

常用跨域方法实践(一)

这几天看了鬼道师兄的<跨终端Web>这本书,该书从解决实际业务出发,讲述了跨终端的优势与未来,受益良多.其中,书中第九章介绍了作者在阿里内部的一个参赛作品Storage,该作品巧妙的使用了跨域知识,让我见识到跨域其实不仅仅是JSONP.以前,对前端跨域这块或多或少有点了解,但真正动手实践的不是很多.于是,这段时间我好好整理了常用跨域方法的具体实现.本文,主要介绍了JSONP和CORS的实现方式. 相关概念: 只要协议.域名.端口有任何一个不同,都被当作是不同的域 JavaScript出于安全方

深入理解前端跨域方法和原理

前言 受浏览器同源策略的限制,本域的js不能操作其他域的页面对象(比如DOM).但在安全限制的同时也给注入iframe或是ajax应用上带来了不少麻烦.所以我们要通过一些方法使本域的js能够操作其他域的页面对象或者使其他域的js能操作本域的页面对象(iframe之间). 这里需要明确的一点是:所谓的域跟js的存放服务器没有关系,比如baidu.com的页面加载了google.com的js,那么此js的所在域是baidu.com而不是google.com.也就是说,此时该js能操作baidu.co

常用跨域资源请求分析

WEB开发过程中最常使用 Ajax技术来完成客户端与服务器的通信.而实现Ajax通信的XmlHttpRequest对象会带来跨域安全策略问题.简单来说,默认情况下,XHR对象只能访问与包含它的页面位于同一个域下的资源. 那么问题来了,何为跨域呢?通常,Ajax指向的地址中,二级域名/端口号/协议/必须与包含它的页面相同.举个栗子: www.tangide.com 访问 www.i5r.com是跨域. a.tangide.com 访问 b.tangide.com是跨域. www.tangide.c

跨域方法总结

有如下的跨域方法,先列出提纲,每个方法的详细,有时间再补充进去 一.JSONP 二.CORS CORS背后的思想,就是使用自定义的http头部让浏览器与服务器进行沟通,从而决定请求或者响应是应该成功还是应该失败. CROS有简单请求 (simple request)和预请求(preflighted request),preflighted request比simple request多一个options的请求 simple request的条件是 (下面两个条件都必须要满足) 1.使用ge或者p

常见跨域方法原理及其用例

一.常见跨域方法 1) JSONP跨域 需要目标服务器配合一个callback函数2) AJAX跨域 CORS3) 使用window.name+iframe来进行跨域 4) window.postMessage:跨文档通信 API(Cross-document messaging)5) 跨子域:修改document.domain6) 通过Nginx反向代理 7) WebSocket 二.原理及其用例 JSONP跨域: 原理: <script>可跨域请求资源,json格式被原生 JavaScri

js中几种实用的跨域方法原理详解(转)

今天研究js跨域问题的时候发现一篇好博,非常详细地讲解了js几种跨域方法的原理,特分享一下. 原博地址:http://www.cnblogs.com/2050/p/3191744.html 下面正文开始 这里说的js跨域是指通过js在不同的域之间进行数据传输或通信,比如用ajax向一个不同的域请求数据,或者通过js获取页面中不同域的框架中(iframe)的数据.只要协议.域名.端口有任何一个不同,都被当作是不同的域. 下表给出了相对http://store.company.com/dir/pag

js中几种实用的跨域方法原理详解

这里说的js跨域是指通过js在不同的域之间进行数据传输或通信,比如用ajax向一个不同的域请求数据,或者通过js获取页面中不同域的框架中(iframe)的数据.只要协议.域名.端口有任何一个不同,都被当作是不同的域. 下表给出了相对http://store.company.com/dir/page.html同源检测的结果: 要解决跨域的问题,我们可以使用以下几种方法: 一.通过jsonp跨域 在js中,我们直接用XMLHttpRequest请求不同域上的数据时,是不可以的.但是,在页面上引入不同

[转]js中几种实用的跨域方法原理详解

转自:js中几种实用的跨域方法原理详解 - 无双 - 博客园 这里说的js跨域是指通过js在不同的域之间进行数据传输或通信,比如用ajax向一个不同的域请求数据,或者通过js获取页面中不同域的框架中(iframe)的数据.只要协议.域名.端口有任何一个不同,都被当作是不同的域. 下表给出了相对http://store.company.com/dir/page.html同源检测的结果: 要解决跨域的问题,我们可以使用以下几种方法: 一.通过jsonp跨域 在js中,我们直接用XMLHttpRequ

[转载] js中几种实用的跨域方法原理详解

框架中(iframe)的数据.只要协议.域名.端口有任何一个不同,都被当作是不同的域. 下表给出了相对http://store.company.com/dir/page.html同源检测的结果: 要解决跨域的问题,我们可以使用以下几种方法: 一.通过jsonp跨域 在js中,我们直接用XMLHttpRequest请求不同域上的数据时,是不可以的.但是,在页面上引入不同域上的js脚本文件却是可以的,jsonp正是利用这个特性来实现的. 比如,有个a.html页面,它里面的代码需要利用ajax获取一