web跨域解決方案


閱讀目錄

摘要:跨域問題,無論是面試還是平時的工作中,都會遇到,本文總結處理跨域問題的幾種方法以及其原理,也讓自己搞懂這方面的知識,走起。

 

什么是跨域

  

    在JavaScript中,有一個很重要的安全性限制,被稱為“Same-Origin Policy”(同源策略)。這一策略對於JavaScript代碼能夠訪問的頁面內容做了很重要的限制,即JavaScript只能訪問與包含它的文檔在同一域下的內容。

  JavaScript這個安全策略在進行多iframe或多窗口編程、以及Ajax編程時顯得尤為重要。根據這個策略,在baidu.com下的頁面中包含的JavaScript代碼,不能訪問在google.com域名下的頁面內容;甚至不同的子域名之間的頁面也不能通過JavaScript代碼互相訪問。對於Ajax的影響在於,通過XMLHttpRequest實現的Ajax請求,不能向不同的域提交請求,例如,在abc.example.com下的頁面,不能向def.example.com提交Ajax請求,等等。

  為什么瀏覽器要實現同源限制?我們舉例說明:

  比如一個黑客,他利用iframe把真正的銀行登錄頁面嵌到他的頁面上,當你使用真實的用戶名和密碼登錄時,如果沒有同源限制,他的頁面就可以通過javascript讀取到你的表單中輸入的內容,這樣用戶名和密碼就輕松到手了.

  又比如你登錄了OSC,同時瀏覽了惡意網站,如果沒有同源限制,該惡意 網站就可以構造AJAX請求頻繁在OSC發廣告帖.

 

跨域的情況分為以下幾種:

  

  特別注意兩點:

  1、如果是協議和端口造成的跨域問題“前台”是無能為力的

  2、在跨域問題上,域僅僅是通過“URL的首部”來識別而不會去嘗試判斷相同的ip地址對應着兩個域或兩個域是否在同一個ip上。比如上面的,http://www.a.com/a.js和http://70.32.92.74/b.js。雖然域名和域名的ip對應,不過還是被認為是跨域。

“URL的首部”指window.location.protocol +window.location.host。其中,

  window.location.protocol:指含有URL第一部分的字符串,如http:

    window.location.host:指包含有URL中主機名:端口號部分的字符串.如//www.cenpok.net/server/

 

常用的幾種跨域處理方法:

 

 1、JSONP

 2、CORS策略

 3、document.domain+iframe的設置

 4、HTML5的postMessage

 5、使用window.name來進行跨域

 

跨域的原理解析及實現方法

 

1、JSONP(JSON with padding)

原理 :

      我們知道,在頁面上有三種資源是可以與頁面本身不同源的。它們是:js腳本,css樣式文件,圖片,像淘寶等大型網站,肯定會將這些靜態資源放入cdn中,然后在頁面上連

接,如下所示,所以它們是可以鏈接訪問到不同源的資源的。

1)<script type="text/javascript" src="某某cdn地址" ></script>

2)<link type="text/css" rel="stylesheet" href="某個cdn地址" />

3)<img src="某個cdn地址" alt=""/>

  而jsonp就是利用了script標簽的src屬性是沒有跨域的限制的,從而達到跨域訪問的目的。因此它的最基本原理就是:動態添加一個<script>標簽來實現。

 

實現方法:

    這里是使用ajax來請求的,看起來和ajax沒啥區別,其實還是有區別的。

    ajax的核心是通過XmlHttpRequest獲取非本頁內容,而jsonp的核心則是動態添加<script>標簽來調用服務器提供的js腳本。

復制代碼
$.ajax({  
        url:"http://crossdomain.com/services.php",  
        dataType:'jsonp',  
        data:'',  
        jsonp:'callback',  
        success:function(result) {  
            // some code
        }  
    });  
復制代碼

上面的代碼中,callback是必須的,callback是什么值要跟后台拿。獲取到的jsonp數據格式如下:

flightHandler({
    "code": "CA1998",
    "price": 1780,
    "tickets": 5
});

jsonp的全稱為json with padding,上面的數據中,flightHandler就是那個padding.

 

 JSONP的不足之處:

  1、只能使用get方法,不能使用post方法:

  我們知道 script,link, img 等等標簽引入外部資源,都是 get 請求的,那么就決定了 jsonp 一定是 get 的。但有時候我們使用的 post 請求也成功,為啥呢?這是因為當我們指定dataType:'jsonp',不論你指定:type:"post" 或者type:"get",其實質上進行的都是 get 請求!

  2、沒有關於 JSONP 調用的錯誤處理。如果動態腳本插入有效,就執行調用;如果無效,就靜默失敗。失敗是沒有任何提示的。例如,不能從服務器捕捉到 404 錯誤,也不能取消或重新開始請求。不過,等待一段時間還沒有響應的話,就不用理它了。

 

 2、CORS策略

 原理:

     CORS是一個W3C標准,全稱是"跨域資源共享"(Cross-origin resource sharing)。它允許瀏覽器向跨源服務器,發出XMLHttpRequest請求,從而克服了AJAX只能同源使用的限制。它為Web服務器定義了一種方式,允許網頁從不同的域訪問其資源.

  CORS系統定義了一種瀏覽器和服務器交互的方式來確定是否允許跨域請求。 它是一個妥協,有更大的靈活性,但比起簡單地允許所有這些的要求來說更加安全。

 

實現方法:

  CORS需要瀏覽器和服務器同時支持。目前,所有瀏覽器都支持該功能,IE瀏覽器不能低於IE10。

  整個CORS通信過程,都是瀏覽器自動完成,不需要用戶參與。對於開發者來說,CORS通信與同源的AJAX通信沒有差別,代碼完全一樣。瀏覽器一旦發現AJAX請求跨源,就會自動添加一些附加的頭信息,有時還會多出一次附加的請求,但用戶不會有感覺。

   

前端方面

以前我們使用Ajax,代碼類似於如下的方式:

var xhr = new XMLHttpRequest(); 
xhr.open("GET", "/hfahe", true); 
xhr.send(); 
// 這里的“/hfahe”是本域的相對路徑。

如果我們要使用CORS,相關Ajax代碼可能如下所示:

var xhr = new XMLHttpRequest(); 
xhr.open("GET", "http://blog.csdn.net/hfahe", true); 
xhr.send(); 
// 請注意,代碼與之前的區別就在於相對路徑換成了其他域的絕對路徑,也就是你要跨域訪問的接口地址。

 

服務器方面
服務器端對於CORS的支持,主要就是通過設置Access-Control-Allow-Origin來進行的。如果瀏覽器檢測到相應的設置,就可以允許Ajax進行跨域的訪問。

 

CORS策略的優缺點:

  優點:

    1、CORS支持所有類型的HTTP請求。

    2、 使用CORS,開發者可以使用普通的XMLHttpRequest發起請求和獲得數據,比起JSONP有更好的錯誤處理。

  缺點: 兼容性方面相對差一點,ie10或以上才支持

 

3、document.domain+iframe的設置  (只有在主域相同的時候才能使用該方法)

原理:

  瀏覽器中不同域的框架之間是不能進行js的交互操作的。但是不同的框架之間(父子或同輩),是能夠獲取到彼此的window對象的,但是,我們也只能獲取到一個幾乎

無用的window對象。比如,有一個頁面,它的地址是 http://www.example.com/a.html , 在這個頁面里面有一個iframe,它的src是 http://example.com/b.html , 很顯然,這

個頁面與它里面的iframe框架是不同域的,所以我們是無法通過在頁面中書寫js代碼來獲取iframe中的東西的。

  這個時候,document.domain就可以派上用場了,我們只要把 http://www.example.com/a.html 和  http://example.com/b.html 這兩個頁面的document.domain都設成

相同的域名就可以了。但要注意的是,document.domain的設置是有限制的,我們只能把document.domain設置成自身或更高一級的父域,且主域必須相同。例如:

a.b.example.com 中某個文檔的document.domain 可以設成a.b.example.com、b.example.com 、example.com中的任意一個,但是不可以設成 c.a.b.example.com,因為這是

當前域的子域,也不可以設成baidu.com,因為主域已經不相同了。

 

使用方法:

  比如在http://www.example.com/a.html 的頁面里要訪問 http://example.com/b.html里面的東西。

  在頁面 http://www.example.com/a.html 中設置document.domain:

復制代碼
//http://www.example.com/a.html
<html>
<head>
    <title>A頁面</title>
    <script type="text/javascript" src="jquery.js"></script>
</head>
<body>
    <div>A頁面</div>
    <iframe id="iframe" src="http://example.com/b.html" style="display:none;"></iframe>
  // 相當於用一個隱藏的iframe來做代理 <script> $(function(){ try{ document.domain = "example.com"; //這里將document.domain設置成一樣 }catch(e){} $("#iframe").load(function(){ var iframe = $("#iframe").contentDocument.$; ifram.get("http://example.com/接口",function(data){}); }); }); </script> <body> </html>
復制代碼

 

在頁面 http://example.com/b.html 中也設置document.domain,而且這也是必須的,雖然這個文檔的domain就是example.com,但是還是必須顯示的設置document.domain的值:

復制代碼
//http://example.com/b.html
<html>
<head>
    <title>B頁面</title>
    <script type="text/javascript" src="jquery.js"></script>
</head>
<body>
    <div>B頁面</div>
    <script>        
        $(function(){
            try{
            document.domain = "example.com"; //這里將document.domain設置成一樣
            }catch(e){}
        });
    </script>
</body>
</html>
復制代碼

  這里有個注意點,就是在A頁面中,要等iframe標簽完成加載B頁面之后,再取iframe對象的contentDocument,否則如果B頁面沒有被iframe完全加載,在A頁面中通過contentDocument屬性就取不到B頁面中的jQuery對象。

      一旦取到B頁面中的jQuery對象,就可以直接發ajax請求了,這種類似“代理”方式可以解決主子域的跨域問題。

缺點:

  只有在主域相同的時候才能使用該方法

4、HTML5的postMessage

原理:

  沒啥原理,就是一個html5所提供的一個API.--->HTML5 window.postMessage是一個安全的、基於事件的消息API。

 

  在需要發送消息的源窗口調用postMessage方法即可發送消息。其中.源窗口可以是全局的window對象,也可以是以下類型的窗口:

  1、文檔窗口中的iframe:

var iframe = document.getElementById('my-iframe');
var win = iframe.documentWindow;

  2、JavaScript打開的彈窗:

var win = window.open();

  3、當前文檔窗口的父窗口:

var win = window.parent;

  4、

var win = window.opener();

 

      發送消息:找到源window對象后,即可調用postMessage API向目標窗口發送消息:

win.postMessage(msg, targetOrigin);

說明:postMessage函數接收兩個參數:

  1、msg, 將要發送的消息,可以使一切javascript參數,如字符串,數字,對象,數組等。

  2、targetOrigin,這個參數稱作“目標域”,注意,是目標域不是本域!比如,你想在2.com的網頁上往1.com網頁上傳消息,那么這個參數就是“http://1.com/”,而不是2.com.協議,(一個完整的域名包括:主機名,端口號。如:http://g.cn:80/)

 

    接收消息:那目標窗口要怎么接收傳過來的數據呢,只要監聽window的message事件就可以接收了。

復制代碼
var onmessage = function (event) {
    var data = event.data;
    var origin = event.origin;
    //do someing
};
if (typeof window.addEventListener != 'undefined') {
    window.addEventListener('message', onmessage, false);
} else if (typeof window.attachEvent != 'undefined') {
    //for ie
    window.attachEvent('onmessage', onmessage);
}
復制代碼

message事件監聽函數接收一個參數,Event對象實例,該對象有三個屬性:

  • data : 消息
  • origin:消息的來源地址
  • source:發送消息窗口的window對象引用

 


使用方法(案例):

  http://test.com/index.html--> 發送消息的頁面

復制代碼
<!-- 這個是 http://test.com/index.html 頁面 -->
<div> <!-- 要給下面的頁面傳一個妹子過去 --> <iframe id="child" src="http://lsLib.com/lsLib.html"></iframe> </div> <script type="text/javascript"> window.onload=function(){ window.frames[0].postMessage('蒼老師','http://lslib.com'); } </script>
復制代碼

 

http://lslib.com/lslib.html --> 接收消息的頁面

復制代碼
<!-- 這個是 http://lslib.com/lslib.html 頁面 -->
<script type="text/javascript">
    window.addEventListener('message',function  (e) {
        console.log(e.origin,e.data);
        alert('收到妹子一枚:'+e.data);
    });
</script>
復制代碼

 

優缺點:

 優點:方便,安全,有效的解決了跨域問題

 缺點:萬惡的資本主義,ie8+才支持,而且ie8+<ie10只支持iframe的方式。

 

 5、使用window.name來進行跨域(相對比較完美的方法)

 原理:  

  當iframe的頁面跳到其他地址時,其window.name值保持不變,並且可以支持非常長的 name 值(2MB)。

  瀏覽器跨域iframe禁止互相調用/傳值.但是調用iframe時 window.name 卻不變,正是利用這個特性來互相傳值,當然跨域下是不容許讀取ifram的window.name值.

       所以這里我們還要准備一個和主頁面http://www.a.com/main.html 相同域下的代理頁面http://www.a.com/other.html ,iframe調用子頁面 http://www.b.com/data.html

 

使用方法:

 1、 准備三個頁面:

  http://www.a.com/main.html   //應用頁面

  http://www.a.com/other.html    // 代理頁面,要求和應用頁面在同一個域。一般是一個空的html

      http://www.b.com/data.html   //應用頁面獲取數據的頁面,簡稱:數據頁面

    2、

     數據頁面將數據傳到window.name中去。如下:

     http://www.b.com/data.html中的 data.html

// data.html
window.name="蒼老師"; //可以是其他類型的數據,比如數組,對象等等

   

    http://www.a.com/main.html   //應用頁面的代碼如下:

復制代碼
<!-- main.html -->
var iframeData;

var state = 0;//開關變量

var iframe = document.createElement('iframe'); //創建iframe

var loadfn = function() {

    if (state === 1) {

        iframeData = iframe.contentWindow.name;  // 讀取數據

        alert('獲取到了iframe傳過來的妹子'+iframeData);

    }else if (state === 0) {

         state = 1;
         iframe.contentWindow.location = 'http://www.a.com/other.html';  //這里是代理頁面 other.html
         
         /**
             這里說明一下:
             由於iframe的location改變了,相當於重新載入頁面(這是iframe的性質決定的),於是重新執行loadfn方法。
        由於當iframe的頁面跳到其他地址時,其window.name值保持不變,並且此時開關變量 state已經變為1,
於是就可以獲取到window.name值,也就達到了跨域訪問的目的了。 **/ }; } iframe.src = 'http://www.b.com/data.html'; //這是是數據頁面,data.html if (iframe.attachEvent) { iframe.attachEvent('onload', loadfn); } else { iframe.onload = loadfn; } document.body.appendChild(iframe);
復制代碼

 

3、獲取數據以后銷毀這個iframe,釋放內存;這也保證了安全(不被其他域frame js訪問)。

    iframe.contentWindow.document.write('');
    iframe.contentWindow.close();
    document.body.removeChild(iframe);

 

優缺點:

  瀏覽器支持情況好,是比較普遍的使用方法

 

 

總結

 

以上總結了js跨域的幾種方法,當然還有其他的方法,不過沒有。他們各有千秋。其實最主要的區別除了實現方式不一樣,主要是瀏覽器的兼容問題而已。

 

JSONP:

       JSONP的優點是:它不像XMLHttpRequest對象實現的Ajax請求那樣受到同源策略的限制;它的兼容性更好,在更加古老的瀏覽器中都可以運行,不需要XMLHttpRequest或ActiveX的支持;並且在請求完畢后可以通過調用callback的方式回傳結果。

      JSONP的缺點則是:它只支持GET請求而不支持POST等其它類型的HTTP請求;它只支持跨域HTTP請求這種情況,不能解決不同域的兩個頁面之間如何進行JavaScript調用的問題。

 

CORS策略

  優點:使用CORS,開發者可以使用普通的XMLHttpRequest發起請求和獲得數據,比起JSONP有更好的錯誤處理。

 

     缺點:古老的瀏覽器不支持,不過大部分現代瀏覽器都支持

 

document.domain+iframe:只適用於主域相同的跨域問題處理

 

html5的postMessage:

  優點html5新引進的特性,可以使用它來向其它的window對象發送消息,無論這個window對象是屬於同源或不同源,目前IE8+、FireFox、Chrome、Opera等瀏覽器都已經支持window.postMessage方法。如果是現代瀏覽器,首選。

  缺點: ie8以前不支持

 

window.name:

  主要是應用當frame的頁面跳到其他地址時,其window.name值保持不變的原理。兼容性好。需要照顧落后的瀏覽器時,首選。、

 

小小總結,有誤之處,歡迎指出

 

如果您覺得文章有用,也可以給咸魚老弟發個微信小額紅包鼓勵,


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM