JavaScript跨域方法

天大地大妈咪最大 提交于 2019-12-04 22:51:34

1 同源策略


同源策略,是由网景(netscape)提出的一个著名的安全策略,现在所有的浏览器都会使用这一安全策略。所谓同源策略,即相互访问的页面之间必须具有相同的协议、端口和主机地址。

    下表给出了相对http://store.company.com/dir/page.html同源检测的结果。     
URL 结果 原因
http://store.company.com/dir2/other.html 成功
http://store.company.com/dir/inner/another.html 成功
https://store.company.com/secure.html 失败 不同协议
http://store.company.com:81/dir/etc.html 失败 不同端口
http://news.company.com/dir/other.html 失败 不同主机(IP)
    由于同源策略的限制,不同域名之间无法通过HTTP请求相互访问。我们把不同域名之间的访问问题称为跨域问题。

2 常用的跨域方法

    本部分主要介绍当前主流的几种跨域方法,包括Cors、JSONP、window.name等几种常见的跨域方法。
    本部分实验部分包括两台服务器,两台服务器ip地址不同,设置的域名分别为fe.xiaojukeji.com(主服务器)、fe_vm.xiaojukeji.com(从
服务器)。

2-1 Cors(Cross-origin resource sharing)


    CORS全称为Cross-origin resource sharing,是一个W3C标准。CORS定义一种跨域访问的机制,可以让AJAX实现跨域访问。CORS 允许一
个域上的网络应用向另一个域提交跨域 AJAX 请求。CORS需要浏览器和服务器同时支持。目前只有Opera Mini和IE10以下不支持。
    整个CORS通信过程,都是浏览器自动完成,不需要用户参与。因此,实现CORS通信的关键是服务器。只要服务器实现了CORS接口,就可以跨源通
信。

CORS工作原理

    CORS有两种请求:简单请求(Simple requests)和预检请求(Preflighted requests),以下部分根据原理图分析介绍CORS的两种请求。

简单请求

满足下列条件就称为简单请求:

  • 请求方法
    • GET
    • POST
    • HEAD
  • 允许设置头部标识(除了用户代理)
    • Accept
    • Accept-Language
    • Content-Langugae
    • Content-Type
  • 允许的Content-Type标识头
    • application/x-www-form-urlencoded
    • multipart/form-data
    • text/plain
    例如,我们从主服务器(fe.xiaojukeji.com)向从服务器(fe_vm.xiaojukeji.com)发送AJAX请求,如下:

主服务器代码

        var xmlhttp = new XMLHttpRequest();
        var url = 'http://fe_vm.xiaojukeji.com/CORS/cors.php';

        function callOtherDomain() {
          if(xmlhttp) {    
            xmlhttp.open('GET', url, true);
            xmlhttp.onreadystatechange = handler;
            xmlhttp.send(); 
          }
        }

        function handler(){
            if(xmlhttp.readyState == 4) {  
                if(xmlhttp.status == 200) {   
                    var response = xmlhttp.responseText;  
                    var divresult = document.getElementById("result");  
                    divresult.innerHTML = response;  
                }  
            }  
        }
        callOtherDomain();
    从服务器httpd.conf配置文件,可以接受http://fe.xiaojukeji.com的跨域访问,修改如下:
    <Directory />
        Options FollowSymLinks
        AllowOverride None
        Header set Access-Control-Allow-Origin http://fe.xiaojukeji.com
    </Directory>

从服务器cors.php代码

    header("Access-Control-Allow-Origin:http://fe.xiaojukeji.com");
    $return = array(
                'login' => 'admin',
                'pass'  => '12345'
            );
    echo json_encode($return);
    浏览器发送跨域AJAX请求,Ajax响应数据如下:
  • 响应结果
    Request URL:http://fe_vm.xiaojukeji.com/CORS/cors.php
    Request Method:GET
    Status Code:200 OK
    Remote Address:*(此处为从服务器IP):80
  • 响应头部
    Access-Control-Allow-Origin:http://fe.xiaojukeji.com
    Connection:close
    Content-Length:32
    Content-Type:text/html; charset=UTF-8
    Date:Wed, 24 Aug 2016 06:57:49 GMT
    Server:Apache/2.2.15 (CentOS)
    X-Powered-By:PHP/5.3.3
  • 请求头部
    Accept:/
    Accept-Encoding:gzip, deflate, sdch
    Accept-Language:zh-CN,zh;q=0.8,en;q=0.6
    Cache-Control:max-age=0
    Connection:keep-alive
    Host:fe_vm.xiaojukeji.com
    Origin:http://fe.xiaojukeji.com
    Referer:http://fe.xiaojukeji.com/CORS/cors_simpleRequest.html
    User-Agent:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36
    Ajax响应数据为在Chrome浏览器的响应结果,请求头部中Origin表示发送Cors请求的源域,host为跨域的目的域。响应头部中Access-Contr
ol-Allow-Origin表示允许http://fe.xiaojukeji.com域名进行跨域访问。

如下图,返回结果已经显示在页面中。
简单请求运行结果

预检请求

    预检请求收先会发送一次包含某些选项头的OPTIONS请求,已确认是否能否安全传送。预符合下面情况的就会被当做预检请求:
  • 使用GET、HEAD或者POST以外的请求方法。
  • 使用POST向服务器端传送数据时,数据类型(Content-Type)为application/x-www-form-urlencoded、multipart/form-data、text/plain三种以外类型。
  • 使用了自定义请求头。
    例如,我们从主服务器(fe.xiaojukeji.com)向从服务器(fe_vm.xiaojukeji.com)发送含有自定义选项的POST请求,如下:

主服务器代码

        var xmlhttp = new XMLHttpRequest();
        var url = 'http://fe_vm.xiaojukeji.com/CORS/cors2.php';
        var body = '<?xml version="1.0"?><person><name>Arun</name></person>';

        function callOtherDomain(){
          if(xmlhttp)
            {
              xmlhttp.open('POST', url, true);
              xmlhttp.setRequestHeader('X-PINGOTHER', 'pingpong');
              xmlhttp.setRequestHeader('Content-Type', 'application/xml');
              xmlhttp.onreadystatechange = handler;
              xmlhttp.send(body); 
            }
        }

        function handler(){
            if(xmlhttp.readyState == 4) {  
                if(xmlhttp.status == 200) {   
                    var response = xmlhttp.responseText;  
                    var divresult = document.getElementById("result");  
                    divresult.innerHTML = response;  
                }  
            }  
        }
        callOtherDomain();

从服务器代码

    header("Access-Control-Allow-Origin:http://fe.xiaojukeji.com");
    header('Access-Control-Allow-Methods:POST');
    header('Access-Control-Allow-Headers:x-pingother,content-type');
    header("Content-Type:application/xml");

    $return = array(
                'login' => 'admin',
                'pass'  => '12345'
            );
    echo json_encode($return);
浏览器发送跨域AJAX请求,Ajax响应数据如下:
  • 第一次请求:options请求
    • 响应结果
      Request URL:http://fe_vm.xiaojukeji.com/CORS/cors2.php
      Request Method:OPTIONS
      Status Code:200 OK
      Remote Address:*(此处为从服务器IP):80
    • 响应头部
      Access-Control-Allow-Headers:x-pingother,content-type
      Access-Control-Allow-Methods:POST
      Access-Control-Allow-Origin:http://fe.xiaojukeji.com
      Connection:close
      Content-Length:32
      Content-Type:application/xml
      Date:Wed, 24 Aug 2016 08:17:02 GMT
      Server:Apache/2.2.15 (CentOS)
      X-Powered-By:PHP/5.3.3
    • 请求头部
      Accept:/
      Accept-Encoding:gzip, deflate, sdch
      Accept-Language:zh-CN,zh;q=0.8,en;q=0.6
      Access-Control-Request-Headers:content-type, x-pingother
      Access-Control-Request-Method:POST
      Cache-Control:max-age=0
      Connection:keep-alive
      Host:fe_vm.xiaojukeji.com
      Origin:http://fe.xiaojukeji.com
      Referer:http://fe.xiaojukeji.com/CORS/cors_preflightedRequests.html
      User-Agent:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36
  • 第二次请求:AJAX POST 请求
    • 响应结果
      Request URL:http://fe_vm.xiaojukeji.com/CORS/cors2.php
      Request Method:POST
      Status Code:200 OK
      Remote Address:10.94.104.114:80
    • 响应头部
      Access-Control-Allow-Headers:x-pingother,content-type
      Access-Control-Allow-Methods:POST
      Access-Control-Allow-Origin:http://fe.xiaojukeji.com
      Connection:close
      Content-Length:32
      Content-Type:application/xml
      Date:Wed, 24 Aug 2016 08:17:02 GMT
      Server:Apache/2.2.15 (CentOS)
      X-Powered-By:PHP/5.3.3
    • 请求头部
      Accept:/
      Accept-Encoding:gzip, deflate
      Accept-Language:zh-CN,zh;q=0.8,en;q=0.6
      Connection:keep-alive
      Content-Length:55
      Content-Type:application/xml
      Host:fe_vm.xiaojukeji.com
      Origin:http://fe.xiaojukeji.com
      Referer:http://fe.xiaojukeji.com/CORS/cors_preflightedRequests.html
      User-Agent:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36
      X-PINGOTHER:pingpong
    第一次请求为OPTIONS请求,表示这个请求是用来询问的。请求头信息有三个关键点:
    (1)Origin为http://fe.xiaojukeji.com,表示请求来自的源。
    (2)Access-Control-Request-Method,表示请求的方法为POST方法。
    (3)Access-Control-Request-Headers表示请求发送的附加信息头字段,例中附加信息头为content-type、x-pingother。
    第一次请求的响应头部中,Access-Control-Allow-Origin为“http://fe.xiaojukeji.com”,表示同意“http://fe.xiaojukeji.com
”域名下的跨域请求。
    第二次请求为POST请求,用来发送正式的请求。第二次请求与简单请求是类似的。这里就不赘述。

附带凭证请求

    默认情况下,在跨域发送AJAX请求,浏览器是不会发送Cookie和HTTP身份凭证信息的,若果需要发送附带Cookie和HTTP身份凭证信息,则需要
设置XMLHttpRequest的withCredentials属性。
    如下所示的例子,主服务器向从服务器发送Ajax发送Get请求,并设置withCredentials属性,传递Cookie。

主服务器代码

        var xmlhttp = new XMLHttpRequest();
        var url = 'http://fe_vm.xiaojukeji.com/CORS/cors3.php';

        function callOtherDomain() {
          if(xmlhttp) {    
            xmlhttp.open('GET', url, true);
            xmlhttp.withCredentials = true;
            xmlhttp.onreadystatechange = handler;
            xmlhttp.send(); 
          }
        }

        function handler(){
            if(xmlhttp.readyState == 4) {  
                if(xmlhttp.status == 200) {   
                    var response = xmlhttp.responseText;  
                    var divresult = document.getElementById("result");  
                    divresult.innerHTML = response;  
                }  
            }  
        }
        callOtherDomain();

从服务器代码

    header("Access-Control-Allow-Origin:http://fe.xiaojukeji.com");
    header("Access-Control-Allow-Credentials: true");
    $return = array(
                'login' => 'admin',
                'pass'  => '12345'
            );
    echo json_encode($return);

浏览器发送跨域AJAX请求,Ajax响应数据如下:

  • 响应结果
    Request URL:http://fe_vm.xiaojukeji.com/CORS/cors3.php
    Request Method:GET
    Status Code:200 OK
    Remote Address:10.94.104.114:80
  • 响应头部
    Access-Control-Allow-Credentials:true
    Access-Control-Allow-Origin:http://fe.xiaojukeji.com
    Connection:close
    Content-Length:32
    Content-Type:text/html; charset=UTF-8
    Date:Wed, 24 Aug 2016 09:39:43 GMT
    Server:Apache/2.2.15 (CentOS)
    X-Powered-By:PHP/5.3.3
  • 请求头部
    Accept:/
    Accept-Encoding:gzip, deflate, sdch
    Accept-Language:zh-CN,zh;q=0.8,en;q=0.6
    Cache-Control:max-age=0
    Connection:keep-alive
    Cookie:uccap=”MTQ3MjAyOTExNjgzMw==”; ucauth_q2=”bGlqaW5jYWk=”; carrenttoken=bGlqaW5jYWkxNDcyMDI5MTIwNDk3; cityId=59; supplierId=24
    Host:fe_vm.xiaojukeji.com
    Origin:http://fe.xiaojukeji.com
    Referer:http://fe.xiaojukeji.com/CORS/cors_requestsWithCredentials.html
    User-Agent:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36
    请求头部中有传递的Cookie值,响应头部中Access-Control-Allow-Credentials: true。一般情况下,ajax请求中必须打开withCred-
entials属性,服务端也必须设置Access-Control-Allow-Credentials:true。两者必须全部设定,否则会发生跨域的错误。
    如果要发送Cookie,Access-Control-Allow-Origin就不能设为星号,必须指定明确的、与请求网页一致的域名。同时,Cookie依然遵循同
源政策,只有用服务器域名设置的Cookie才会上传,其他域名的Cookie并不会上传,且(跨源)原网页代码中的document.cookie也无法读取服务器
域名下的Cookie(本部分引用于跨域资源共享CORS详解)。

相关HTTP响应头

Access-Control-Allow-Origin: Orgin | *
Access-Control-Expose-Headers: X-My-Custom-Header, X-Another-Custom-Header
Access-Control-Max-Age: delta-seconds
Access-Control-Allow-Credentials: true|false
Access-Control-Allow-Methods:method[, method]*
Access-Control-Allow-Headers: field-name[, field-name]*

相关HTTP请求头

Origin: origin
Access-Control-Request-Method: method
Access-Control-Allow-Headers: field-name[, field-name]*


2-2 JSONP(JSON with Padding)

JSONP(JSON with Padding)是一个非官方的协议,它允许在服务器端集成Scripttags返回至客户端,通过javascript callback的形式实现跨域访问。

    jsonp的应用基础是script标签中的外部js函数是可以被执行或者调用的。如常用引用<script src="//cdn.bootcss.com/jquery/3.1.0
/jquery.js" ></script>,我们可以调用jquery的相关函数。
    依据实例给出JSONP的客户端具体实现(本部分的思路与链接9思路相同,感觉链接9作者提供的思路):

(1) 远程调用JS,执行跨域中JS的代码。

本部分主要说明跨域中的JS函数是可以调用的。

主服务器代码

    var localHandler = function(data){
            alert('我是主服务器上函数,从服务器的数据是:' + data.result);
    };

从服务器代码

    localHandler({"result":"我是从服务器上的数据!!"});

执行主服务器的html,得到执行结果如下:
执行结果

    页面成功弹出提示窗口,显示主服务器的远程js调用成功,并且还接收到了远程js带来的数据。现在面临的问题就是跨域服务器如何知道的call-
back函数。

(2)向跨域服务传递callback函数,要求跨域服务器返回需求的JS函数。

本部分主要通过原生JS完成JSONP协议。

主服务器代码

     function jsonpCallback(result) {  
        var html = '';
        for(var i in result) {  
            html += '<div><span>'+i+':</span><p>'+result[i]+'</p></div>';
        }  
        console.log(html);
        document.getElementById('showTips').innerHTML = html;
    }  
    var JSONP=document.createElement("script");  
    JSONP.type="text/javascript";  
    JSONP.src="http://fe_vm.xiaojukeji.com/jsonp/jsonp.php?jsoncallback=jsonpCallback";  
    document.getElementsByTagName("head")[0].appendChild(JSONP);  

从服务器代码

    header('Content-Type:text/html;Charset=utf-8');  
    $arr = array(  
        "user" => 'admin',
        "pass" => '123456'
    );  
   echo $_GET['jsoncallback'] . "(".json_encode($arr).")"; 
    主服务器中,向从服务器发送GET请求,请求的返回函数为jsoncallback。从服务器后端代码使用php函数获取函数名称定义$_GET['jsonca-
llback']函数,传递的数据为json_encode($arr)。HTTP请求完成后,jsonpCallback函数执行,执行后showTips,显示返回数据内容。

执行结果如下
JSONP跨域执行结果
(3)JQuery JSONP跨域

本部分主要介绍jquery的jsonp跨域方法

    JQuery将JSONP协议集合与AJAX请求中,极大方便了我们的前端开发。

主服务器代码

        $(function(){  
            $.ajax(  
                {  
                    type:'get',  
                    url : 'http://fe_vm.xiaojukeji.com/jsonp/jsonp.php',  
                    dataType : 'jsonp',  
                    data:{
                        loginuser: "admin",
                        loginpass: "123456"
                    },
                    jsonp:"jsoncallback",  
                    success  : function(data) {  
                        var html = "<div>用户名:"+ data.user+"</div>";
                        html +=  "<div>密码:"+ data.pass+"</div>";
                         $("#showTips").html(html);
                    },  
                    error : function() {  
                        $("#showTips").html('error connect!'); 
                    }  
                }  
            );  
        });  

从服务器代码

    header('Content-Type:text/html;Charset=utf-8');  
    $arr = array(  
        "user" => 'admin',
        "pass" => '123456'
    );  
   echo $_GET['jsoncallback'] . "(".json_encode($arr).")"; 
    主服务器中并未定义jsoncallback,但是仍然能执行,这主要是因为jquery将jsonp归入了ajax,自动帮你生成回调函数并把数据取出来供
success属性方法来调用。

2-3 window.name


Window.name属性的优势在于:name值在不同的页面加载后依然存在且支持非常长的name值(2MB)。

    由于window.name存在时间及可访问性,window.name可被用于页面信息的传递及跨域信息的传递中。下图为window.name的传输原理,依据
此原理图,window.name的传输技术的基本原理和步骤如下:

window传输原理

(1)客户机浏览器创建隐藏iframe,加载跨域站点,跨域站点设置window.name=传递信息。
(2)客户机监控onload事件,iframe站点加载完成后,由于无法访问跨域的iframe,此时需要设置iframe,返回到客户机的原始域中。
(3)返回到原始域中,触发新的onload事件,解析获取得到传输的data数据,并且删除添加的iframe,完成一次信息的传输。

    本文按照链接9的思路做了相关的实验,验证window.name实现跨域。

主服务器代码

        (function(){
            dataRequest = {
                _doc: document,
                cfg: {
                    proxyUrl: 'http://fe.xiaojukeji.com/WINDOWNAME/windowName.html'
                }
            };

            //surl 跨域的url,fnCallBack回调函数
            dataRequest.send = function(sUrl, fnCallBack){
                if(!sUrl || typeof sUrl !== 'string'){
                    return;
                }

                sUrl += (sUrl.indexOf('?') > 0 ? '&' : '?') + 'windowname=true';
                var frame = this._doc.createElement('iframe'), state = 0, self = this;
                frame.id = 'iframe';
                this._doc.body.appendChild(frame);
                frame.style.display = 'none';

                //删除添加的iframe
                var clear = function(){
                    try{
                        frame.contentWindow.document.write('');
                        frame.contentWindow.close();
                        self._doc.body.removeChild(frame);
                    }catch(e){}
                };
                //获取数据
                var getData = function(){
                    try{
                        var da = frame.contentWindow.name;
                    }catch(e){}
                    clear();
                    if(fnCallBack && typeof fnCallBack === 'function'){
                        fnCallBack(da);
                    }
                };
                //onload事件
                document.getElementById('iframe').onload =  function(){
                    if(state === 1){
                        getData();
                    } else if(state === 0){
                        state = 1;
                        frame.contentWindow.location = self.cfg.proxyUrl;
                    }
                };

                frame.src = sUrl;
            };    
        })();

        function callback(da){
            document.getElementById('showTips').innerHTML = da;
        }

        var crossUrl ='http://fe_vm.xiaojukeji.com/windowName/window_name.html'

        var dataRequest2 = dataRequest.send(crossUrl,callback);

从服务器代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>window.name测试</title>
</head>
<body>
    <p id="htmlContent">  
            some <strong>html/xml-style</strong>data  
    </p>  
</body>
    <script type="text/javascript">
        //普通字符串
        window.name = "我是从服务器数据,现在传递给主服务器";

        //XML/HTML数据
        window.name = document.getElementById("htmlContent").innerHTML; 

        //JSON
        var temp= {
           foo:"bar", 
           baz:"foo"
        };      
        window.name = JSON.stringify(temp);   
    </script>
</html>

执行结果

执行结果

    window.name可以实String、JSON和HTML/XML数据的传递。跨域的HTML(从服务器)window.name设置的值,主服务器在切换到自身域后,
可以调用读取window.name,从而实现跨域的信息传递。

2-4 HTML5 postMessage和onMessage


在 HTML5 中提出了工作线程(Web Workers)的概念,并且规范出 Web Workers的三大主要特征:能够长时间运行(响应),理想的启动性能以及理想的内存消耗。Web Workers 为 Web前端网页上的脚本提供了一种能在后台进程中运行的方法。一旦它被创建,Web Workers 就可以通过postMessage向任务池发送任务请求,执行完之后再通过 postMessage 返回消息给创建者指定的事件处理程序 ( 通过onmessage 进行捕获)(详情参见链接11)。

    使用 postMessage和onMessage,不仅同源(域 + 端口号)的 Web 网页之间可以互相通信,甚至可以实现跨域通信。在接收窗口使用
onmessage 事件进行监听,发送窗口通过 postMessage 方法来传递数据。该方法使用两个参数:data、origin,data为发送的消息样本
、origin是可接受的窗口(*代表任何窗口都能接受)。
    设计实验验证H5 postMessage可以实现跨域的消息传递(实验代码参照链接11)。

主服务器

 <html> 
 <head> 
 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> 
 <title>使用H5实现跨域</title> 
 <script type="text/JavaScript"> 
     function sendIt(){ 
         // 通过 postMessage 向子窗口发送数据
         document.getElementById("otherPage").contentWindow 
             .postMessage( 
                 document.getElementById("message").value, 
                "http://fe_vm.xiaojukeji.com"
             ); 
     } 
 </script> 
 </head> 
 <body> 
     <!-- 通过 iframe 嵌入子页面 --> 
     <iframe src="http://fe_vm.xiaojukeji.com/h5Post/h5Post.html" 
                 id="otherPage" style="width:100%;"></iframe> 
     <br/><br/> 
     <input type="text" id="message"><input type="button" 
             value="发送来自从服务器" onclick="sendIt()" /> 
 </body> 
 </html>

从服务器代码

 <html> 
 <head> 
 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> 
 <title>Web page from child.com</title> 
 <script type="text/JavaScript"> 
     //event 参数中有 data 属性,就是父窗口发送过来的数据
     window.addEventListener("message", function( event ) { 
         // 把父窗口发送过来的数据显示在子窗口中
       document.getElementById("content").innerHTML+=event.data+"<br/>"; 
     }, false ); 

 </script> 
 </head> 
 <body> 
     页面内容 http://fe_vm.xiaojukeji.com/h5Post/h5Post.html
     <div id="content"></div> 
 </body> 
 </html>

实验结果

这里写图片描述

     主服务器向子窗口发送PostMessage message信息,从服务器代码添加'message'的监控事件。一旦主服务器运行发送Message消息,从服务
域message接收主服务器发送的信息,从而完成跨域通信。

2-5 document.domain + iframe


相同主域名不同子域名下的页面,可以设置 document.domain 让它们同域,从而实现跨域通信。但是这种方法必须在主域名相同、域名协议和端口一致,否则无法实现跨域通信。

    两个域名下页面需要交互时,如http://fe.xiaojukeji.com/DOMAIN/domain.html需要和http://fe_vm.xiaojukeji.com/domain/
domain_iframe.html页面进行通信前者通过添加iframe,设置两者的document.domain,完成两个页面的通信。
    本文设计相关实验,验证主域名相同、协议与端口相同下,通过设置document.domain完成苦与通信。

主服务器代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>domain实现跨域</title>
    <script type="text/javascript" src="jquery-1.7.1.js"></script>
    <script type="text/javascript">
        document.domain = "xiaojukeji.com"; //设置成主域
        function testDomain(){
            var win = document.getElementById('iframe').contentWindow,
                doc = win.document,
                content = doc.body;
             document.getElementById('showTips').innerHTML = content.innerHTML;
        }
    </script>
</head>
<body>
    <h1>iframe显示数据</h1>
    <iframe src="http://fe_vm.xiaojukeji.com/domain/iframe.html" onload='testDomain()' id="iframe"></iframe>
    <h1>获取iframe数据显示</h1>
    <div class="divShow" id="showTips" style="border:solid 2px #FF2;">

    </div>
</body>
</html>

从服务器代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>iframe实现跨域</title>
    <script type="text/javascript">
        document.domain = "xiaojukeji.com"
    </script>
</head>
<body>
    <h1>hello world!!!</h1>
</body>
</html>

实验结果
运行结果截图

在主服务器、从服务器的页面中,我们都设置document.domain=“xiaojukeji.com”,从而使两个页面的域名相同,从而可以实现跨域通信。这里需要注意的是document.domain的设置必须与域名的主域名一致,否则document.domain的设置是非法的。如document.domain=‘Alibaba.com’,这肯定是不合法的,无法实现跨域通信。

2-6 location.hash+iframe


location.hash 属性是一个可读可写的字符串,该字符串是 URL 的锚部分(从 # 号开始的部分)。例如当前的 URL 是 http://fe.xiaojukeji.com:1234/test.htm#part2,则location.hash的值为part2。

原理示意图

三个页面之间传递参数用的是location.hash,改变hash并不会导致页面刷新。本文参考链接13设计实验,检查location.hash的跨域效果。

主服务器

locationHash.html
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>location.hash实现跨域</title>
        <script type="text/javascript">
            //通过动态创建iframe的hash发送请求
            function sendRequest(){
              var ifr = document.createElement('iframe');
              ifr.style.display = 'none';
              //跨域发送请求给html, 参数是sayHello
              ifr.src = 'http://fe_vm.xiaojukeji.com/hash/hash.html#sayHello';
              document.body.appendChild(ifr);
            }
            //获取返回值的方法
            function checkHash() {
              var data = location.hash ?
                 location.hash.substring(1) : '';
              if (data) {
                //处理返回值
                alert(data);
                location.hash='';
              }
            }
            //定时检查自己的hash值
            setInterval(checkHash, 2000);
            window.onload = sendRequest;
        </script>
    </head>
    <body>
</body>
</html>

loctionHash2.html
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>locations hash实现跨域</title>
        <script type="text/javascript">
            //location2 和 location2s属于同一个域,
            // 可通过parent.parent获取window对象
            parent.parent.location.hash =
                self.location.hash.substring(1);
        </script>
    </head>
    <body>
    </body>
    </html>

从服务器代码

hash.html
    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <title>domain实现跨域</title>
      <script type="text/javascript">
        function checkHash(){
          var data = '';
          //模拟一个简单的参数处理操作
          switch(location.hash){
            case '#sayHello': data = 'hello world!';break;
            default: break;
          }
          data && callBack('#'+data);
        }
        function callBack(hash){
          // ie、chrome的安全机制无法修改parent.location.hash,
          // 所以要利用一个中间的fe.xiaojukeji.com域下的代理iframe
          var proxy = document.createElement('iframe');
          proxy.style.display = 'none';
          proxy.src = 'http://fe.xiaojukeji.com/HASH/locationHash2.html'+hash;
          document.body.appendChild(proxy);
        }
        window.onload = checkHash;
      </script>
    </head>
    <body>
    </body>
    </html>

运行结果截图
这里写图片描述

页面通信示意图

引用块内容

    结合页面通信示意图,我们可以得到使用location.hash进行跨域通信的过程如下:
    (1)主页面locationHash(外层棕黄色部分),首选创建iframe,并设置src=“http://fe_vm.xiaojukeji.com/hash/hash.html#sayHe
llo”,设置定时检查locationHash主页面的hash值。
    (2)hash.html(绿色部分)获取自身页面的hash值,根据主页面传递的hash返回相应的结果(具体可参见代码)。在数据转换完成后,创建新的if
rame,设置src=http://fe.xiaojukeji.com/HASH/locationHash2.html'+hash。iframe与主页面locationHash是同一域名下。
    (3)代理页面(内层棕黄色部分),将自身hash赋值给主页面locationHash的hash,完成信息的传递。parent.parent.location.hash 等同
于 locationHash.hash,self.location.hash等同于代理页面的hash值。
    (4)主页面定时检查,检查得到hash的变化,输出变化值,从而完成一次跨域的信息传递。

3 参考文献

[1]同源策略
[2]wiki-同源策略
[3]Cross-origin resource sharing
[4]Cross-origin resource sharing.
[5]跨域共享学习笔记
[6]跨域资源共享CORS详解
[7]CORS协议
[8]JSONP协议
[9]JSONP详解
[10]window.name跨域
[11]HTML5 postMessage 和 onmessage API 详细应用
[12]location.hash
[13]location.hash+iframe跨域
[14]webSocket 跨域

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!