[跨域]前端解決跨域問題

1.同源策略以下:

URL 說明 是否容許通訊
http://www.a.com/a.js
http://www.a.com/b.js
同一域名下 容許
http://www.a.com/lab/a.js
http://www.a.com/script/b.js
同一域名下不一樣文件夾 容許
http://www.a.com:8000/a.js
http://www.a.com/b.js
同一域名,不一樣端口 不容許
http://www.a.com/a.js
https://www.a.com/b.js
同一域名,不一樣協議 不容許
http://www.a.com/a.js
http://70.32.92.74/b.js
域名和域名對應ip 不容許
http://www.a.com/a.js
http://script.a.com/b.js
主域相同,子域不一樣 不容許
http://www.a.com/a.js
http://a.com/b.js
同一域名,不一樣二級域名(同上) 不容許(cookie這種狀況下也不容許訪問)
http://www.cnblogs.com/a.js
http://www.a.com/b.js
不一樣域名 不容許
特別注意兩點:
第一,若是是協議和端口形成的跨域問題「前臺」是無能爲力的,
第二:在跨域問題上,域僅僅是經過「URL的首部」來識別而不會去嘗試判斷相同的ip地址對應着兩個域或兩個域是否在同一個ip上。
「URL的首部」指window.location.protocol +window.location.host,也能夠理解爲「Domains, protocols and ports must match」。

 瀏覽器都有一個同源策略,其限制之一就是第一種方法中咱們說的不能經過ajax的方法去請求不一樣源中的文檔。 它的第二個限制是瀏覽器中不一樣域的框架之間是不能進行js的交互操做的。javascript

 

 

2前端解決跨域問題

如下的例子包含的文件均爲爲 http://www.a.com/a.html http://www.a.com/proxy.html 與 http://www.b.com/b.html,要作的都是從a.html獲取b.html裏的數據php

一 JSONP

 原理:jsonp是利用script標籤沒有跨域限制的特性,經過在srcurl的參數上附加回調函數名字,而後服務器接收回調函數名字並返回一個包含數據的回調函數html

         經過script標籤引入一個js文件,這個js文件載入成功後會執行咱們在url參數中指定的函數,而且會把咱們須要的json數據做爲參數傳入。因此jsonp是須要服務器端的頁面進行相應的配合的。前端

 示例:html5

    function doSomething(data) {
        // 對data處理
    }
    var script = document.createElement("script");
    script.src = "http://www.b.com/b.html?callback=doSomething";
    document.body.appendChild(script);

    // 1.生成一個script標籤,將其append在body上,向服務器發出請求
    // 2.服務器根據 callback 這個參數生成一個包含數據的函數 doSomething({"a", "1"})
    // 3.頁面事先已聲明doSomething函數,此時執行 doSomething(data) 這個函數,得到數據

 

二 window.name + iframe

原理:java

window對象有個name屬性,該屬性有個特徵:即在一個窗口(window)的生命週期內,窗口載入的全部的頁面都是共享一個window.name的,每一個頁面對window.name都有讀寫的權限,window.name是持久存在一個窗口載入過的全部頁面中的,並不會因新頁面的載入而進行重置。web

window.name 的美妙之處:name 值在不一樣的頁面(甚至不一樣域名)加載後依舊存在,而且能夠支持很是長的 name 值(2MB)。ajax

window.name的原理是利用同一個窗口在不一樣的頁面共用一個window.name,這個須要在a.com下創建一個代理文件proxy.html,使同源後a.html能獲取proxy.htmlwindow.namejson

name 在瀏覽器環境中是一個全局/window對象的屬性,且當在 frame 中加載新頁面時,name 的屬性值依舊保持不變。經過在 iframe 中加載一個資源,該目標頁面將設置 frame 的 name 屬性。此 name 屬性值可被獲取到,以訪問 Web 服務發送的信息。但 name 屬性僅對相同域名的 frame 可訪問。這意味着爲了訪問 name 屬性,當遠程 Web 服務頁面被加載後,必須導航 frame 回到原始域。同源策略依舊防止其餘 frame 訪問 name 屬性。一旦 name 屬性得到,銷燬 frame 。 segmentfault

示例:

a.com/a.html

  var iframe = document.createElement("iframe");
    iframe.src = "http://www.b.com/b.html";
    document.body.appendChild(iframe); // 如今a.html裏建一個引用b.html的iframe,得到b的數據

    var flag = true;
    iframe.onload = function() {
        if (flag) {
            iframe.src = "proxy.html";             
   // 判斷是第一次載入的話,這時候iframe的window.name就含有所但願得到的數據,這時候設置iframe和a.html在同源,才能獲取iframe的window.name的值,改變一個iframe的地址(src屬性)從新加載後觸發onload事件,第二次載入進入時flag爲false進入else循環
            flag = false;
        } else { // 第二次載入因爲a和proxy同源,a能夠直接獲取proxy的window.name
            alert(iframe.contentWindow.name);

            iframe.contentWindow.close();
            document.body.removeChild(iframe);
            iframe.src = '';
            iframe = null;
        }
    }

b.com/b.html

window.name = "這是 b 頁面的數據";

Web 服務器如何提供 window.name 數據

爲了讓 Web 服務器實現 window.name,服務器應該只尋找請求中是否包含 windowname 參數。若是包含了 windowname 參數,服務器應該返回一個設置了 window.name 字符串值的 HTML 文檔,迴應此請求並傳送到客戶端。例如:
http://www.a.com/getdata.html?windowname=true

若是服務器想用 Hello 響應客服端,它應該返回一個 HTML 頁面:

<html>     <script type="text/javascript">         window.name="Hello";     </script> </html>

一樣也能夠轉換爲 JSON 數據:

<html>     <script type="text/javascript">         window.name='{"foo":"bar"}';     </script> </html>

  

 

三 document.domain(同屬於同一個基礎域名的兩個域名之間可用)

原理:

兩個屬於同一個基礎域名的域名的 document.domain設置成自身或更高一級的父域,使得document.domain相同,這樣經過在頁面中書寫js代碼來獲取iframe中的東西

 

瀏覽器都有一個同源策略,其限制之一就是第一種方法中咱們說的不能經過ajax的方法去請求不一樣源中的文檔。 它的第二個限制是瀏覽器中不一樣域的框架之間是不能進行js的交互操做的。有一點須要說明,不一樣的框架之間(父子或同輩),是可以獲取到彼此的window對象的,但蛋疼的是你卻不能使用獲取到的window對象的屬性和方法(html5中的postMessage方法是一個例外,還有些瀏覽器好比ie6也可使用top、parent等少數幾個屬性),總之,你能夠當作是隻能獲取到一個幾乎無用的window對象。好比,有一個頁面,它的地址是http://www.example.com/a.html  , 在這個頁面裏面有一個iframe,它的src是http://example.com/b.html, 很顯然,這個頁面與它裏面的iframe框架是不一樣域的,因此咱們是沒法經過在頁面中書寫js代碼來獲取iframe中的東西

利用document.domain 實現跨域:
前提條件:這兩個域名必須屬於同一個基礎域名!並且所用的協議,端口都要一致,不然沒法利用document.domain進行跨域

 

有另外一種狀況,兩個子域名:

aaa.xxx.com

bbb.xxx.com

aaa裏的一個網頁(a.html)引入了bbb 裏的一個網頁(b.html),這時a.html裏一樣是不能操做b.html裏面的內容的。由於document.domain不同,一個是aaa.xxx.com,另外一個是bbb.xxx.com。這時咱們就能夠經過Javascript,將兩個頁面的domain改爲同樣的,須要在a.html裏與b.html裏都加入:document.domain = "xxx.com";這樣這兩個頁面就能夠互相操做了。也就是實現了同一基礎域名之間的"跨域"。

示例:

 

a.com/a.html

  document.domain = "a.com";
    var iframe = document.createElement("iframe");
    iframe.src = "http://b.a.com/b.html";
    document.body.appendChild(iframe);
    iframe.onload = function() {
        console.log(iframe.contentWindow....); // 在這裏操做b.html裏的元素數據 這樣咱們就能夠經過js訪問到iframe中的各類屬性和對象了。
    }

 b.a.com/b.html

document.domain = "a.com";

注意:document.domain須要設置成自身或更高一級的父域,且主域必須相同。

 不過若是你想在http://www.example.com/a.html 頁面中經過ajax直接請求http://example.com/b.html 頁面,即便你設置了相同的document.domain也仍是不行的,因此修改document.domain的方法只適用於不一樣子域的框架間的交互。若是你想經過ajax的方法去與不一樣子域的頁面交互,除了使用jsonp的方法外,還能夠用一個隱藏的iframe來作一個代理。原理就是讓這個iframe載入一個與你想要經過ajax獲取數據的目標頁面處在相同的域的頁面,因此這個iframe中的頁面是能夠正常使用ajax去獲取你要的數據的,而後就是經過咱們剛剛講得修改document.domain的方法,讓咱們能經過js徹底控制這個iframe,這樣咱們就可讓iframe去發送ajax請求,而後收到的數據咱們也能夠得到了。

 

 

四 HTML5的postMessage

原理:

 

假設a.html裏嵌套個<iframe src="http://www.b.com/b.html" frameborder="0"></iframe>,在這兩個頁面裏互相通訊

 示例:

 a.com/a.html

 window.onload = function() {
        window.addEventListener("message", function(e) {
            alert(e.data);
        });
      //  window.frames[0].postMessage("b data", "http://www.b.com/b.html");  
    }

 b.com/b.html

window.onload = function() {
   //      window.addEventListener("message", function(e) {
  //          alert(e.data);
 //       });
        window.parent.postMessage("a須要的數據", "http://www.a.com/a.html");
    }

 

 

五 CORS

CORS是一個W3C標準,全稱是"跨域資源共享"(Cross-origin resource sharing)克服了AJAX只能同源使用的限制

CORS背後的思想,就是使用自定義的HTTP頭部讓瀏覽器與服務器進行溝通,從而決定請求或響應是應該成功,仍是應該失敗。

 

CORSXMLHttpRequest Level 2 裏規定的一種跨域方式。在支持這個方式的瀏覽器裏,javascript的寫法和不跨域的ajax寫法如出一轍,

只要服務器須要設置Access-Control-Allow-Origin: * 或者請求頭中Origin的源信息

(ajax跨域請求時,會自動在請求頭部添加一個 Origin: http://www.ccd.com 協議 域名 端口 以便於服務器根據這個頭部信息來決定是否給予響應,因此關鍵在於服務器的設置)

若是沒有這個頭部,或者有這個頭部可是源信息不匹配,瀏覽器就駁回請求

 

六 websocket

web sockets是一種瀏覽器的API,它的目標是在一個單獨的持久鏈接上提供全雙工、雙向通訊。(同源策略對web sockets不適用)

web sockets原理:在JS建立了web socket以後,會有一個HTTP請求發送到瀏覽器以發起鏈接。取得服務器響應後,創建的鏈接會使用HTTP升級從HTTP協議交換爲web sockt協議。

只有在支持web socket協議的服務器上才能正常工做。

var socket = new WebSockt('ws://www.baidu.com');//http->ws; https->wss
socket.send('hello WebSockt');
socket.onmessage = function(event){
    var data = event.data;
}

 

七 window.location.hash + iframe

原理:

 b.html將數據以hash值的方式附加到proxy.htmlurl上,在proxy.html頁面經過location.hash獲取數據後傳到a.html(這個例子是傳到a.htmlhash上,固然也能夠傳到其餘地方)

 

示例:

a.com/a.html

 var iframe = document.createElement("iframe");
    iframe.src = "http://www.b.com/b.html"; document.body.appendChild(iframe); // 在a頁面引用b function check() { // 設置個定時器不斷監控hash的變化,hash一變說明數據傳過來了 var hashs = window.location.hash; if (hashs) { clearInterval(time); alert(hashs.substring(1)); } } var time = setInterval(check, 30);

 

b.com/b.html

  window.onload = function() {
        var data = "this is b's data"; var iframe = document.createElement("iframe"); iframe.src = "http://www.a.com/proxy.html#" + data; document.body.appendChild(iframe); // 將數據附加在proxy.html的hash上 }

 

a.com/proxy.html

// 獲取自身的hash再傳到a.html的hash裏,數據傳輸完畢
parent.parent.location.hash = self.location.hash.substring(1); 

 

八 經過script標籤加載

對於瀏覽器來講,script標籤的src屬性所指向資源就跟img標籤的src屬性所指向的資源同樣,都是一個靜態資源,瀏覽器會在適當的時候自 動去加 載這些資源,而不會出現所謂的跨域問題。這樣咱們就能夠經過該屬性將要訪問的數據對象引用進當前頁面而繞過js跨域問題。 例如,須要在 hi域 下管理中心頁面中隨機推薦幾個熱門模塊給用戶,因爲熱門模塊的相關信息都在 act域 下的php模塊中維 護,若是直接在hi域下經過ajax請求去獲取act域下的推薦模塊列表相關信息就出現js跨域問題。解決這個問題的最簡單方法就是,在hi域下經過 script標籤去訪問act域提供的這個http接口:

<script type=」text/javascript」 src=」http://act.hi.baidu.com/widget/recommend」><script>

 

參考:https://segmentfault.com/a/1190000003882126

   http://www.cnblogs.com/JChen666/p/3399951.html

相關文章
相關標籤/搜索