• 跨域浅谈


      说到跨域,我们就不得不先提一下同源。

      同源是浏览器的一种安全策略,所谓同源是指,域名,协议,端口完全相同,而不同源就是跨域。也就是说我们如果域名,协议,端口只要有一个不是不同的那么就是跨域。

      举个例子说:http://www.example.com/

    http://api.example.com/detail.html          不同源 域名不同  
    https//www.example.com/detail.html          不同源 协议不同  
    http://www.example.com:8080/detail.html     不同源    端口不同  
    http://api.example.com:8080/detail.html     不同源    域名、端口不同  
    https://api.example.com/detail.html         不同源    协议、域名不同  
    https://www.example.com:8080/detail.html    不同源    端口、协议不同  
    http://www.example.com/detail/index.html    同源    只是目录不同  
    

      通过上面的例子,相信大家都对同源和跨域有了一定的了解。浏览器出于安全考虑,同源策略不允许跨域调用其他页面的对象。但是安全限制的同时也给我们使用iframe或者获取其他服务器上的接口数据带来了不少的麻烦。

      因为我们在所难免的会需要调用其他服务器的接口,所以提供了一下几个跨域的方案。

    解决iframe跨域

    方案一:document.domain

      前面我们说到浏览器的同源策略限制了不同源的iframe之间进行的DOM操作,但是需要说明的一点是,在不同的iframe之间(父子或同级)是能够获取到彼此window对象的。

    document.domain解决了在顶级域名相同的情况下但是域名不完全相同的跨域问题.

      比如:我们有一个页面它的地址是: http://www.study.com/domain.html 在这个页面中有一个有一个iframe,它的src是http://api.study.com/domain.html,代码如下

    <body>
        <p>我是父窗口study.com的内容</p>
        <iframe id="iframe" src="http://api.study.com/domain.html" frameborder="0">
        </iframe>
        <script>
            // 这种情况适合 顶级域名相同的情况
            document.domain = 'study.com';
    
            var iframe = document.getElementById('iframe');
    
            iframe.onload = function () {
                var contentWin = iframe.contentWindow;
    
                contentWin.document.getElementById('txt').style.color = 'red';
            }
        </script>
    </body>
    

      很显然这个页面和它里面的iframe来自不同的域,我们默认是无法相互操作对方的DOM元素的,但是如果我们都将两个页面的document.domain 设为相同的域名即:document.domain = 'study.com'就可以操作对方DOM元素了。iframe页面的代码如下

    <body>
        <p id="txt">我是api.study.com域下的一个页面</p>
        <script>
            document.domain = 'study.com';
    
            // 访问父级
            top.document.getElementsByTagName('p')[0].style.color = 'red';
        </script>
    </body>
    

      需要说明的是:document.domain的设置是有限制的,我们只能把document.domain设置成自身或更高一级的父域,且顶级域名必须相同。 例如:a.b.study.com 中的某个页面的document.domain 可以设成a.b.study.com、b.study.com 、study.com中的任意一个,但是不可以设成 c.a.b.study.com,因为这是当前域的子域,也不可以设成baidu.com,因为顶级域名已经不相同了。

      在实现了两个页面的DOM元素访问也就相当于实现了两个页面之间的数据传递,这个就需要我们一起发散思维了。

    方案二:window.name

      我们前面说到在不同的iframe之间(父子或同级)是能够获取到彼此window对象的。我们可以通过window.name实现的跨域数据传输。window对象有个name属性,该属性有个特征:即在一个窗口(window)的生命周期内,窗口载入的所有页面都是共享一个window.name的,每个页面对window.name都有读写权限,window.name是持久存在一个窗口载入过的所有页面中的,并不会因新页面的的载入而进行重置。

      效果实现: 我们需要准备三个页面:

      www.study.com/name.html //应用页面。

    <body>
        <!-- setp 1 -->
        <iframe id="iframe" src="http://api.study.com/name.html" frameborder="0">
        </iframe>
        <button id="btn">获取数据</button>
        <script>
            var iframe = document.getElementById('iframe');
    
            // 将数据填加到name上了
            iframe.contentWindow.name = 100;
    
            iframe.onload = function () {
                this.src = 'proxy.html';
                this.onload = null;
            }
    
            var btn = document.getElementById('btn');
            btn.onclick = function () {
                var text = iframe.contentWindow.name;
                document.write('<p>我是从api.study.com中得到值' + text + '</p>');
            }
        </script>
    </body>
    

      www.study.com/proxy.html //代理文件,一般是一个没有任何内容的html文件,需要和应用页面在同一域下。在应用页面动态创建iframe

      api.study.com/name.html //应用页面需要获取数据的页面,可称为数据页面。

    <body>
        <p id="txt">我是api.study.com域下的一个页面</p>
        <script>
            window.name = 400;
        </script>
    </body>
    

    方案三:location.hash

      大家都知道location是javascript里边BOM中管理地址栏的内置对象,比如location.href就管理页面的url,用location.href=url就可以直接将页面重定向url。而location.hash则可以用来获取或设置页面的标签值。比如http://domain/#admin的location.hash="#admin"。

      它的原理和window.name有些类似都需要通过一个同源文件作为代理文件,和window.name一样我们也需要准备三个页面。

      www.study.com/hash.html //应用页面。

    <body>
      <script type="text/javascript">
        function getData(url, fn) {
          var iframe = document.createElement('iframe');
          iframe.style.display = 'none';
          iframe.src = url;
    
          iframe.onload = function() {
            fn(iframe.contentWindow.location.hash.substring(1));
            window.location.hash = '';
            document.body.removeChild(iframe);
          };
    
          document.body.appendChild(iframe);
        }
    
        // get data from server
        var url = 'http://api.study.com/hash.php';
        getData(url, function(data) {
          var jsondata = JSON.parse(data);
          console.log(jsondata.name + ' ' + jsondata.age);
        });
      </script>
    </body>
    

      www.study.com/proxy.html //代理文件,需要和应用页面在同一域下。

      api.study.com/hash.php //应用页面需要获取数据的页面,可称为数据页面。

    <?php
       // 如果有必要则进行数据处理 $_GET['..']
       // code
    
       // 返回的数据
       $data = '{"name":"zs","age":10}';
    
      echo "<script>
         window.location = 'http://localhost/proxy.html' + '#' + "$data";
       </script>";
    ?>
    

    方案四:HTML5中新引进的window.postMessage方法来跨域传送数据

      window.postMessage(message,targetOrigin) 方法是html5新引进的特性,可以使用它来向其它的window对象发送消息,无论这个window对象是属于同源或不同源,目前IE8+、FireFox、Chrome、Opera等浏览器都已经支持window.postMessage方法。

      postMessage(data,origin)方法接受两个参数

      1.data:要传递的数据,html5规范中提到该参数可以是JavaScript的任意基本类型或可复制的对象,然而并不是所有浏览器都做到了这点儿,部分浏览器只能处理字符串参数,所以我们在传递参数的时候需要使用JSON.stringify()方法对对象参数序列化,在低版本IE中引用json2.js可以实现类似效果。

      2.origin:字符串参数,指明目标窗口的源,协议+主机+端口号[+URL],URL会被忽略,所以可以不写,这个参数是为了安全考虑,postMessage()方法只会将message传递给指定窗口,当然如果愿意也可以建参数设置为"*",这样可以传递给任意窗口,如果要指定和当前窗口同源的话设置为"/"。

      这次我们只需要两个页面即可

      www.study.com/postMessage.html

    <body>
        <iframe id="iframe" src="http://api.study.com/postmessage.html" frameborder="0">
        </iframe>
    
        <script>
            var iframe = document.getElementById('iframe');
    
            iframe.onload = function () {
                // 向哪个域下传值
                iframe.contentWindow.postMessage('red', 'http://api.study.com');
            }
        </script>
    </body>
    

      api.study.com/possMessage.html

    <body>
        <p id="txt">我是api.study.com域下的一个页面</p>
        <script>
            window.addEventListener('message', function(ev) {
                document.getElementById('txt').style.color = ev.data;
            });
        </script>
    </body>
    

    方案五:JSONP

      全名为:JSON with Padding

      我们都知道link 、 script 、img 标签都有跨域的能力,而jsonp的本质就是利用了script标签的可跨域的特性,由服务端返回一个预先定义好的Javascript函数的调用,并且将服务器数据以该函数参数的形式传递过来,此方法需要前后端配合完成。

      前端页面:www.study.com/jsonp.html

    <script>
        function fuck(data){
            var data = JSON.parse(data);
            console.log(data);
        }
    </script>
    <script src="http://api.study.com/jsonp.php?callback=fuck"></script>
    

      后台页面api.study.com/jsonp.php

    <?php 
        header('Content-Type:text/html;charset=utf-8');
    
        $callback = $_GET['callback'];
    
        $json = '{"name":"xjj","age":"10"}';
    
        echo $callback."('$json')";
    ?>
    

      在我们常用的jquery中的调用时集合在了ajax方法中,

      将设置dataType值为jsonp即开启跨域访问

      jsonp 可以指定服务端接收的参数的“key”值,默认为callback

        jsonpCallback 可以指定相应的回调函数,默认自动生成

      前端调用  www.study.com/jquery_jsonp.html

    $.ajax({
        type:'get',
        url:'http://api.study.com/jquery_jsonp.php',
        dataType:'jsonp',
        jsonp:'callback',
        success:function(data){
            console.log(data);
        }
    });
    

      后台页面 api.study.com/jquery_jsonp.php

    <?php
        header('Content-Type:text/html;charset=utf-8');
    
        /*处理业务逻辑  返回数据给第三方过的的接口*/
    
        $callback = $_GET['callback'];
    
        $json = '{"name":"xjj","age":"18"}';
    
        echo $callback.'('.$json.')';
    ?>
    

    方案六:CORS: 跨域资源共享(Cross-Origin Resource Sharing)

      当前几乎所有的浏览器(Internet Explorer 8+, Firefox 3.5+, Safari 4+和 Chrome 3+)都可通过名为跨域资源共享(Cross-Origin Resource Sharing)的协议支持ajax跨域调用。

    启用 CORS 请求

      假设您的页面在 www.study.com 上了,而您想要从 www.learn.com 提取数据。一般情况下,如果您尝试进行这种类型的 AJAX 调用,请求将会失败,而浏览器将会出现“源不匹配”的错误。利用 CORS,www.learn.com 服务端只需添加一个HTTP Response头,就可以允许来自 www.study.com 的请求:

    Access-Control-Allow-Origin: http://example.com
    Access-Control-Allow-Credentials: true(可选)
    

      可将 Access-Control-Allow-Origin 添加到某网站下或整个域中的单个资源。要允许任何域向你提交请求,请设置如下:

    Access-Control-Allow-Origin: *
    Access-Control-Allow-Credentials: true(可选)
    

      启用开发人员工具后,您就会在响应中看到 Access-Control-Allow-Origin 。

    提交跨域请求

      如果服务器端已启用了 CORS,那么提交跨域请求就和普通的 XMLHttpRequest 请求没什么区别。例如,现在 example.com 可以向 www.example2.com 提交请求了:

    $.ajax({
        type:'get',
        url:'http://api.study.com/CORS.php',
        dataType:'jsonp',
        success:function(data){
            console.log(data);
        }
    });
    

      CORS在移动终端支持的不错,可以考虑在移动端全面尝试;PC上有不兼容和没有完美支持。

      CORS提供了一种跨域请求方案,但没有为安全访问提供足够的保障机制,如果你需要信息的绝对安全,不要依赖CORS当中的权限制度,应当使用更多其它的措施来保障。

  • 相关阅读:
    TCP/UDP 协议,和 HTTP、FTP、SMTP,区别及应用场景
    ZeroMQ使用汇总
    C/C++ 笔试、面试题目大汇总
    Caffe+Windows 环境搭建收集
    轻松看懂机器学习十大常用算法
    Caffe学习系列——工具篇:神经网络模型结构可视化
    深度神经网络可视化工具集锦
    MIT一牛人对数学在机器学习中的作用给的评述
    A Full Hardware Guide to Deep Learning
    Caffe上手教程
  • 原文地址:https://www.cnblogs.com/jingh/p/5922398.html
Copyright © 2020-2023  润新知