溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

老生常談的跨域處理

發(fā)布時間:2020-09-07 19:56:05 來源:腳本之家 閱讀:122 作者:咸魚老弟 欄目:web開發(fā)

閱讀目錄

  • 什么是跨域
  • 常用的幾種跨域處理方法:
  • 跨域的原理解析及實現(xiàn)方法
  • 總結

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

什么是跨域

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

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

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

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

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

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

老生常談的跨域處理

特別注意兩點:

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

 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來進行跨域

跨域的原理解析及實現(xiàn)方法

1、JSONP(JSON with padding)

原理 :

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

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

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

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

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

實現(xiàn)方法:

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

ajax的核心是通過XmlHttpRequest獲取非本頁內容,而jsonp的核心則是動態(tài)添加<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 調用的錯誤處理。如果動態(tài)腳本插入有效,就執(zhí)行調用;如果無效,就靜默失敗。失敗是沒有任何提示的。例如,不能從服務器捕捉到 404 錯誤,也不能取消或重新開始請求。不過,等待一段時間還沒有響應的話,就不用理它了。

2、CORS策略

原理:

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

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

實現(xiàn)方法:

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

整個CORS通信過程,都是瀏覽器自動完成,不需要用戶參與。對于開發(fā)者來說,CORS通信與同源的AJAX通信沒有差別,代碼完全一樣。瀏覽器一旦發(fā)現(xiàn)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(); 
// 請注意,代碼與之前的區(qū)別就在于相對路徑換成了其他域的絕對路徑,也就是你要跨域訪問的接口地址。

服務器方面

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

CORS策略的優(yōu)缺點:

優(yōu)點:

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

 2、 使用CORS,開發(fā)者可以使用普通的XMLHttpRequest發(fā)起請求和獲得數據,比起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" ></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對象,就可以直接發(fā)ajax請求了,這種類似“代理”方式可以解決主子域的跨域問題。

缺點:

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

4、HTML5的postMessage

原理:

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

在需要發(fā)送消息的源窗口調用postMessage方法即可發(fā)送消息。其中.源窗口可以是全局的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();

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

win.postMessage(msg, targetOrigin);

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

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

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

接收消息:那目標窗口要怎么接收傳過來的數據呢,只要監(jiān)聽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事件監(jiān)聽函數接收一個參數,Event對象實例,該對象有三個屬性:

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

使用方法(案例):

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

<!-- 這個是 http://test.com/index.html 頁面 -->
<div>
  <!-- 要給下面的頁面?zhèn)饕粋€妹子過去 -->
  <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>

優(yōu)缺點:

優(yōu)點:方便,安全,有效的解決了跨域問題

缺點:萬惡的資本主義,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'); //創(chuàng)建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的性質決定的),于是重新執(zhí)行l(wèi)oadfn方法。
        由于當iframe的頁面跳到其他地址時,其window.name值保持不變,并且此時開關變量 state已經變?yōu)?,
       于是就可以獲取到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);

優(yōu)缺點:

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

總結

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

JSONP:

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

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

CORS策略

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

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

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

html5的postMessage:

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

缺點: ie8以前不支持

window.name:

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

以上就是本文的全部內容,希望本文的內容對大家的學習或者工作能帶來一定的幫助,同時也希望多多支持億速云!

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內容(圖片、視頻和文字)以原創(chuàng)、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI