日期:2014-05-16  浏览次数:20408 次

近乎完美的简单 JS 跨域解决方式 --window.name
当然,“近乎完美”仅仅是个人观点,但如下所述,它确实简单而颇有效益!

一直在寻求一种自己满意的 JS 跨域方式(这里是指任意跨域),曾经了解过:

  1. 即时插入 script 元素的方式,会让脚本立即执行,不安全,并且需要与跨域的远端做好约定——比如变量名。细节较为繁琐。
  2. 写 iframe 的 location.hash 的方式,会导致历史记录的产生,且数据量有限,同时,因为 URL 的内容可视,既不好看也容易泄露信息。
  3. 用代理? 虽然算是最“正宗”的完整跨域方案,但太麻烦了点——首先得有代理,如果量大的话,代理的负担会很重,会导致“瓶颈”制约。
  4. 利用 Flash 跨域,不在考虑之列——复杂了些,并且那不算是 JS 跨域。

无意中看到一篇文章《使用 window.name 解决跨域问题》……豁然开朗! 俺的问题终于解决了。

对那个实现小改了一下,加了浏览器本地设置 Window.name 的功能,实现浏览器“本地”异域数据的传递(如从浏览器窗口A传递数据到窗口B,仅在本地进行,且两个域不同)——这会产生一个很蛙王的应用(下一篇文章《普通 http 网络下数据的安全传输》将详细说明,本文后有少量提及)。感谢网络! 也感谢愿意分享的技术达人!

下面贴出俺的代码,也分享一下,呵呵……
(注意:本地需要创建一个名为 proxy.html 的空文件)


(function() {

    var _isIE = (
        navigator.appName == "Microsoft Internet Explorer"
    );

    var _removeNode = _isIE ? function() {
        var d;
        return function(n) {
            if(n && n.tagName != 'BODY') {
                d = d || document.createElement('div');
                d.appendChild(n);
                d.innerHTML = '';
            }
        }
    }() : function(n) {
        if(n && n.parentNode && n.tagName != 'BODY') {
            n.parentNode.removeChild(n);
        }
    };


/* [ Request by window.name ]
 * ****************************************************************************
   借助 Window.name 实现 Js 的跨域访问。
   1、 url 向外传值, callback 处理返回结果。
   2、 返回页面中 JS 对 window.name 赋值。

   返回页
   <script language="JavaScript">
       window.name = ...  // 支持 JSON 字符串,可达~2MB
   </script>

   若需同时进行多个请求,回调函数应是不同的函数实例。
   iframe 的自由载入形成了异步机制。
*/

    wnRequest = {
        _doc: document,
        _proxyUrl: 'proxy.html'
    };

    wnRequest.send = function( url, callback )
    {
        if(! url || typeof url !== 'string') {
            return;
        }
        url += (url.indexOf('?') > 0 ? '&' : '?') + 'windowname=get';

        var frame = this._doc.createElement('iframe');
        frame._state = 0;
        this._doc.body.appendChild(frame);
        frame.style.display = 'none';

        (function( el, type, fn ) {
            if (_isIE) {
                el.attachEvent('on' + type, fn);
            } else {
                el.addEventListener(type, fn, false);
            }
        })(frame, 'load', function() {
            if(frame._state == 1) {
                _getData(frame, callback);
            } else if(frame._state == 0) {
                frame._state = 1;
                //frame.contentWindow.location = wnRequest._proxyUrl;
                frame.contentWindow.location.replace(wnRequest._proxyUrl);
            }
        });
        frame.src = url;
    };

    //
    // 设置异域 Js 可访问的本地数据,客户端直接站间转递数据
    // 注:
    // 即浏览器直接将数据转递给另一个域的窗口,数据不上网。
    // 返回页代码:
    // <script type="text/javascript">
    //     if (window.name) {
    //         //... 处理 name 值
    //         window.name = null;
    //     }
    //     // 升为顶级窗口,完成数据转递
    //     try {
    //         top.location.hostname;
    //         if (top.location.hostname != window.location.hostname) {
    //             top.location.href =window.location.href;
    //         }
    //     } catch(e) {
    //         top.location.href = window.location.href;
    //     }
    // </script>
    //
    //
    wnRequest.setname = function( name, url ) {
        if(! url || typeof url !== 'string') {
            return;
        }
        url += (url.indexOf('?') > 0 ? '&' : '?') + 'windowname=loc';

        var frame = this._doc.createElement('iframe');
        frame._count = 0;
        this._doc.body.appendChild(frame);
        frame.style.display = 'none';
        if (_isIE) {
            frame.name = name;
        } else {
            frame.contentWindow.name = name;
        }
        frame.src = url;
    };

    //
    // 私用辅助
    //
    var _clear = function(frame) {
        try {