1. 程式人生 > >關於跨域的5種解決方案的知識

關於跨域的5種解決方案的知識

跨域

同源策略
首先從同源策略開始講
----何謂同源:相同的域名、相同的協議、相同的埠即為同源,若有其一不同則視為不同域。

----同源策略帶來的限制:
1.cookie、localstorage、indexDB無法讀取
2.DOM和JS物件無法讀取
3.AJAX請求無法傳送;

有時需要跨域請求資源,那麼對於這一需求,有什麼辦法解決同源策略的限制呢?
1. jsonp(jsonp with padding)
jsonp和json的區別:
----json返回的的是一串資料;jsonp返回的是指令碼程式碼
----jsonp只能用get請求,不能用post請求
在JS中,XMLHttpRequest物件無法直接請求跨域的資源,但是在頁面上引入不同域上的JS指令碼檔案是可以的。jsonp利用該特性,具體實現思路是:在頁面新增一個script標籤 ,利用SRC屬性獲取對指定地址的請求,這點就類似是get請求。該請求會返回一個JS檔案,檔案載入成功後會執行在src的URL中指定的函式,並且會把需要的json資料作為引數傳入。

<script>
 function doSomething(jsonData) {
     ...................
}
</script>
<script src="http://example.com/data.php?callback=doSomething"></script>

利用jQuery實現jsonp
jQuery會自動生成一個全域性函式替換callback=?中的問號,在獲取到資料後會自動銷燬。而$.getJSON方法會自動判斷是否跨域,不跨域就會呼叫普通的ajax方法;若是跨域會以非同步載入JS檔案的形式來呼叫jsonp的回撥函式。

    <script>
    $.getJSON('http://example.com/data/php?callback=?',function(jsondata){
                 //處理獲得的資料
     })

具有src屬性的標籤都可以實現跨域

2. CORS(cross-origin resource sharing)
cors分為簡單請求和非簡單請求
簡單請求滿足的條件:
---- 使用特定的請求方式如head、get、post
----並且請求頭資訊為:accept、accept-language、content-language、last-event-ID、content-type
普通跨域請求:伺服器設定Access-Control-Allow-Origin即可。
帶cookie請求的:前後端都要設定欄位。
特點


—cors支援所有型別的http請求,大多數瀏覽器支援cors除了IE6、IE7
—可以使用普通的XMLHttpRequest發起請求和獲得資料,比jsonp有更好的錯誤處理
實現的思路:使用自定義的http頭部,伺服器根據瀏覽器的origin值來決定是否同意此次請求。

  瀏覽器請求頭:Origin: http://foo.example
  伺服器響應: Access-Control-Allow-Origin: http://foo.example[*]   //表明可以被來自http://foo.example的域訪問[若為*,表示可以被任意外域訪問]

簡單請求是使用特定的方式請求資料;非簡單請求使用設定的方式請求資料之前先發送一個options請求,試探伺服器是否允許客戶端傳送非簡單請求,預檢通過後會再一次傳送請求用於資料傳輸,如下圖:
在這裡插入圖片描述
詳細介紹:
https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Access_control_CORS

3. document.domain+iframe
跨域一般分為:一種xhr不能訪問不同源的文件,一種是不同的window之間是不可以進行互動操作的如頁面中嵌入的iframe。
document.domain解決的是不同window之間不可以互動操作的情況。
案例:現在有一個頁面A,地址是http://example.com/a.html,在該A頁面中有一個 iframe,它的src是B頁面的地址http://example.com/b.html,A頁面與iframe是不同域的,所以現在帶來的問題是無法在A頁面中書寫JS程式碼獲取iframe中的內容

A頁面
<script>
   function test() {
          var  iframe = document.getElementById('iframe');
          var win = document.contentWindow;
          var  doc = win.document;  //獲取不到
          var name = win.name;  //獲取不到
   }
   </script>
   <iframe id="iframe" src="http://example.com/b.html" onload="text()"></frame>

解決辦法:在A頁面的http://example.com/a.html和http://example.com/b.html的document.domain都設為相同的域名即可。如下:

A頁面
<script>
document.domain=‘example.com’; //設為主域!!!!
   function test() {
   //可以打印出
         console.log(document.getElementById('iframe').contentWindow);
   }
   </script>
   <iframe id="iframe" src="http://example.com/b.html" onload="text()"></frame>
B頁面
<script>
document.domain="example.com"; // iframe載入的B頁面的JS中也設定domain
   function testB() {
   }
   </script>

注意:document.domain 的設定是有限制的,只能將domain設定為自身或者更高一級的父域,並且主域要相同,如上述的example.com是主域,如果設定為baidu.com則主域就不相同了。

4. window. name+iframe
window物件有個name屬性,name屬性的特徵是在一個視窗的生命週期內,視窗載入的所有的頁面會共享一個 window.name,每個頁面都可以對window.name進行讀寫。
特點:相容所有的瀏覽器; window中所有的頁面都可以進行修改;window.name的值只可以是字串的形式,最大是2M的容量。

A頁面
<script>
window.name="我是A頁面設定的值";
setTimeout(function() {
       window.location="b.html"
},3000);
</script>
B頁面
<script>
alert(window.name);
</script>

如何實現跨域?假設現在A頁面是 www. example. com/a.html。需要利用A頁面中的JS獲取不同域的B頁面 www. csdn. com/b.html中的window. name的值。
b.html程式碼如下:

<script>
window.name="我是A想要的資料";
</script>

要實現在A頁面中不跳轉獲取到B頁面的資料的解決辦法:1.在A頁面中嵌入iframe。利用iframe獲取b.html的資料,然後A再從iframe中獲取到想要的資料。
2. iframe要獲取到 b.html的資料需要將 iframe標籤的 src設定為B頁面的地址 www. csdn. com/b.html。3. 需要再將 iframe的src屬性設為與A頁面同一個域的頁面地址( 因為頁面中嵌入的iframe和該頁面不屬於同源)。4. 然後在A頁面的 JS程式碼裡處理從B頁面獲取的資料,處理資料的程式碼完畢。
A頁面程式碼如下:

A頁面
<script>
var  state = 0;//設定標誌位來進行iframe與A頁面同源的操作。
function getData(){
      var iframe = document.getElementById('proxy');
      iframe.onload = function() {
          if(state===0){
                        iframe.src = "http://www.example.com/proxy.html"; //與A頁面同源的某個代理頁面,內容可以為空
                        state = 1;
          }else if(state===1){
                  var data = iframe.contentWindow.name;  //利用iframe的window.name獲取B頁面中設定的資料
                  console.log(data);   //打印出“我就是A想要的資料”
                  destroyIframe();  //呼叫函式銷燬iframe
            }
      }
      
      function  destroyIframe() {
                   iframe.contentWindow.close(); //處理完畢後最好關閉iframe,釋放記憶體
                  iframe.contentWindow.close();
                  document.body.removeChild(iframe);
      }
 }
<body>
<iframe id='proxy' src="http://csdn.com/b.html" style="display:none" onload="getData()"></iframe>
</body>

proxy.html頁面:

proxy.html 代理頁
內容可以為空
  1. postMessage
    HTML5引入的新特性,用於:頁面和新開啟的視窗的資料傳遞;多視窗之間訊息傳遞;頁面與嵌入的iframe訊息傳遞。
    用法:postMessage(data,origin)
    data:傳遞的資料,最好用JSON.stringify()進行序列化,部分瀏覽器只支援字串。
    origin:協議+主機(http預設為www)+埠或者設定為*表示傳遞給任意視窗;如指定與當前視窗同源設定“/”。
A頁面www.domain2.com/a.html
<iframe id="iframe" src="http://www.domain2.com/b.html" style="display:none;"></iframe>
<script>       
    var iframe = document.getElementById('iframe');
    iframe.onload = function() {
        var data = {
            name: 'aym'
        };
        // 向domain2傳送跨域資料!!!!
        iframe.contentWindow.postMessage(JSON.stringify(data), 'http://www.domain2.com');
    };
B頁面www.domain2.com/b.html
<script>
    // 接收domain1的資料
    window.addEventListener('message', function(e) {
        alert('data from domain1 ---> ' + e.data);

        var data = JSON.parse(e.data);
        if (data) {
            data.number = 16;

            // 處理後再發回domain1
            window.parent.postMessage(JSON.stringify(data), 'http://www.domain1.com');
        }
    }, false);
</script>

參考資料:
https://blog.csdn.net/tjcjava/article/details/76468225
https://www.cnblogs.com/2050/p/3191744.html
前端常見跨域解決方案(全)http://www.cnblogs.com/roam/p/7520433.html