zoukankan      html  css  js  c++  java
  • 跨域通信方法总结

    本文总结了5种较常见的跨域通信方法,如下:

    1)jsonp

    2)CORS(Cross OriginResource Sharing,跨源资源共享)

    3)主域相同可以设置document.domain

    4)利用window.name实现跨域

    5)利用window.name实现跨域

    jsonp

    讲解jsonp之前先看一个例子:假设域A.com上有一个页面a.html,代码如下:

    var dosomething= function(data){
        alert('我是A.com域上的页面,可以被跨域的remote.js文件调用,远程js带来的数据是:' + data.result);
    };
    </script>
    <script src="B.com/b.js"></script>

    而B.com上的b.js文件代码如下:

    dosomething({"result":"我是远程js带来的数据"});

    运行a.html,显然会弹出弹框,显示接受到了远程js带来的数据。

    以上代码在A域上声明了处理数据的函数,在B域上将json数据传入此函数,实现调用。但是,我们如何在B域上知道A域定义的函数名呢,这就需要我们在请求脚本文件时将函数名一同发送给B,告诉B“我想要一段调用XXX函数的js代码,请你返回给我”,B获取此函数名,并将数据传入此函数名中进行调用,而这就是jsonp的原理。为了更通用也灵活的使用此方案,可以采用动态创建script的方式,从而避免手动填写script地址。以下看看a.html如何实现:

    var dosomething = function(data){
            //处理数据
        };
        // 提供jsonp服务的url地址(不管是什么类型的地址,最终生成的返回值都是一段javascript代码)
        var url = "http://B.com/b.php?callback=dosomething";
        // 创建script标签,设置其属性
        var script = document.createElement('script');
        script.setAttribute('src', url);
        // 把script标签加入head,此时调用开始
        document.getElementsByTagName('head')[0].appendChild(script);

    假设B域的中json数据在b.php中,则b.php的实现大致如下:

    <?php 
    $callback = $GET['callback'];//获得函数名
    $data = array('a','b','c');//获得数据
    echo $callback.'('.json_encode($data).')';//执行函数,并输出结果
    ?>

    这样,我们就将获得的数据传入了约定的函数中,jsonp的过程也就顺利完成。

    优点:1)简单易用,兼容性好,可以在老版浏览器中运行;

               2)能够直接访问响应文本,支持在浏览器与服务器之间双向通信。

    缺点:1)只支持GET请求,而不支持POST等其他请求;

               2)安全性不高,若其他域不安全包含恶意代码,除了放弃jsonp调用,没有办法追究;

              3)确定jsonp请求失败较难,虽然html5给script标签新增了onerror事件,但浏览器支持程度还不高。

    CORS(Cross OriginResource Sharing,跨源资源共享)

    由于同源策略,XHR无法访问其他域的资源。CORS的主要思想就是,使用自定义的HTTP头部告诉服务器,可以接受指定域的访问,也就相当于在约定好的情况下临时接触了特定域的同源策略。

    假设A.com域要访问B.com的数据,只需在B.com做如下设置即可

    header("Access-Control-Allow-Origin:http://A.com");

    表示B.com可以接受来自A.com的访问请求,相当于临时接触了同源限制,接下来的访问操作就和不跨域一样。

    优点:支持所有类型的http请求,可以使用普通XHR对象请求数据,比jsonp有更好的错误处理机制;

    缺点:浏览器支持度不高。

    document.domain+iframe

    (此访问只适用于主域相同,而子域不同的场景。比如www.A.com和script.A.com)

    此方法只需要将如上所述的两个域中的页面设置相同的document.domain,并在www.A.com/a.html中创建一个iframe,将此iframe的地址设置为script.A.com域地址,以此来操控script.A.com。www.A.com/a.html的代码:

    document.domain = 'A.com';
    var ifr = document.createElement('iframe');
    ifr.src = 'http://script.A.com/b.html';
    ifr.style.display = 'none';
    document.body.appendChild(ifr);
    ifr.onload = function(){
        var doc = ifr.contentDocument || ifr.contentWindow.document;
        // 在这里操纵b.html
    };

    script.A.com/b.html的代码

    document.domain = 'A.com';

    利用window.name实现跨域

    window.name有个特征:即在一个窗口(window)的生命周期内,窗口载入的所有的页面都是共享一个window.name的,每个页面对window.name都有读写的权限,window.name是持久存在一个窗口载入过的所有页面中的,并不会因新页面的载入而进行重置。比如:

    有一个a.html,代码如下:

    window.name='我是a.html的Window.name';
    window.location ="b.html";

    b.html代码如下:

    alert(window.name);

    运行a.html,会跳转到b.html,并弹出弹窗显示”我是a.html的Window.name“。也就是只要b.html不对window.name做修改,其值都是a.html中设置的window.name值。

    仔细回顾一下以上过程,我们会发现,可以利用这个window.name来传输我们需要在不同域中传输的数据。比如A.com/a.html要访问B.com/b.html数据,那么可以在B.com/b.html将数据放在window.name中:

    window.name='我是要传输的数据';

    但在A.com/a.html,显然不能通过window.location来跳转到B.com/b.html,我们希望可以不用跳转就可以获得数据。此时我们需要借助一个隐藏的媒人iframe,利用iframe去获取B.com/b.html的数据,然后A.com/a.html再去获取iframe得到的数据。

    <iframe id="proxy" src="B.com/b.html" style="display: none" onload =loadfn></iframe>
    <script>
        var state = 0;
        var iframe = document.getElementById('proxy');
        loadfn = function() {
            if (state === 1) {
                var data = iframe.contentWindow.name;    // 读取数据
                alert(data);    //弹出'我是要传输的数据'
            } else if (state === 0) {
                state = 1;
                iframe.src = "http://A.com/proxy.html";//设置的代理文件,只要与A.com同域即可
            }  
        };
    </script>

    上述过程首先将iframe的src设置为B.com/b.html,这样就可以获取B.com/b.html的window.name,也就是需要的数据。然后,如果A.com/a.html想要获得此数据,必须是的iframe与A.com同域,因此采用一个与A.com同域的代理页面A.com/proxy.html去获得此时iframe的contentWindow.name值,也就是之前获得了之后一直没有改变的window.name值。

    HTML5的window.postMessage实现跨域

    window.postMessage(message,targetOrigin)  方法是html5新引进的特性,可以使用它来向其它的window对象发送消息,无论这个window对象是属于同源或不同源。

    window.postMessage(message,targetOrigin) 中的window指的接受数据的window对象,一般是页面中iframe的contentWindow属性,第一个参数message为要发送的消息,类型只能为字符串;第二个参数targetOrigin表示接收数据的那个window对象所在的域。

    比如A.com/a.html要访问B.com/b.html的数据,则由B.com/b.html向A.com/a.html发送数据,B.com/b.html的实现如下:

    <iframe id="ifr" src="A.com/a.html"></iframe>
    <script type="text/javascript">
    window.onload = function() {
        var ifr = document.getElementById('ifr');
        var targetOrigin = 'http://A.com/a.html';  
        ifr.contentWindow.postMessage('我是数据', targetOrigin);
    };
    </script>

    在A.com/a.html中监听message事件,从而获得发送过来的数据:

    window.addEventListener('message', function(event){
            // 通过origin属性判断消息来源地址
            if (event.origin == 'http://A.com/a.html ') {
                alert(event.data);    // 弹出"我是数据"
            }
        }, false);
  • 相关阅读:
    【maven】maven源码打包
    【分布式事务】阿里fescar
    第五章 mybatis批量更新update
    第二十九章 springboot + zipkin + mysql
    第二十八章 springboot + zipkin(brave定制-AsyncHttpClient)
    第二十七章 springboot + zipkin(brave-okhttp实现)
    附8 zipkin
    第二十六章 hystrix-dashboard + turbine
    附7 turbine
    第二章 部署war包到tomcat
  • 原文地址:https://www.cnblogs.com/youhong/p/6878634.html
Copyright © 2011-2022 走看看