跨域淺談

  說到跨域,咱們就不得不先提一下同源。javascript

  同源是瀏覽器的一種安全策略,所謂同源是指,域名,協議,端口徹底相同,而不一樣源就是跨域。也就是說咱們若是域名,協議,端口只要有一個不是不一樣的那麼就是跨域。php

  舉個例子說:http://www.example.com/html

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或者獲取其餘服務器上的接口數據帶來了很多的麻煩。前端

  由於咱們在所不免的會須要調用其餘服務器的接口,因此提供了一下幾個跨域的方案。html5

解決iframe跨域

方案一:document.domain

  前面咱們說到瀏覽器的同源策略限制了不一樣源的iframe之間進行的DOM操做,可是須要說明的一點是,在不一樣的iframe之間(父子或同級)是可以獲取到彼此window對象的。java

document.domain解決了在頂級域名相同的狀況下可是域名不徹底相同的跨域問題.jquery

  好比:咱們有一個頁面它的地址是: http://www.study.com/domain.html 在這個頁面中有一個有一個iframe,它的src是http://api.study.com/domain.html,代碼以下web

<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頁面的代碼以下ajax

<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,由於頂級域名已經不相同了。json

  在實現了兩個頁面的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當中的權限制度,應當使用更多其它的措施來保障。

相關文章
相關標籤/搜索