前端跨域解決方案

什麼是跨域

瀏覽器從一個域名的網頁去請求另外一個域名的資源時,域名、端口、協議任一不一樣,都是跨域,它是由瀏覽器的同源策略形成的,是瀏覽器對JavaScript實施的安全限制。javascript

1 . 經過document.domain跨域

  • 概念php

    該屬性是一個只讀的字符串,包含了載入當前文檔的 web 服務器的主機名。html

  • 特色html5

    只有在一級域名相同的狀況下才能夠實現跨域。java

  • 代碼web

/**
 * 父域:http://b.com/b.com.html內容
 */
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<script> document.domain = 'b.com'; var ifr = document.createElement('iframe'); ifr.src = 'http://a.b.com/a.b.com.html'; ifr.style.display = 'none'; document.body.appendChild(ifr); ifr.onload = function(){ var doc = ifr.contentDocument || ifr.contentWindow.document; // 這裏操做DOM var oUl = doc.getElementById('ul1'); alert(oUl.innerHTML); ifr.onload = null; } </script>
</body>
</html>

/**
 * 子域:http://a.b.com/a.b.com.html
 */
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<script> document.domain = 'b.com'; </script>
<ul id="ul">我是子域a.b.com中的UL</ul>
</body>
</html>
複製代碼

2 . 經過HTML5的postMessage方法跨域

  • 概念算法

    是html5引入的API能夠更方便、有效、安全的解決跨域問題。postMessage()方法容許來自不一樣源的腳本採用異步方式進行有限的通訊,能夠實現跨文本檔、多窗口、跨域消息傳遞。json

  • 語法後端

    /** * [description] * @param {[string]} message 其餘窗口的一個引用,好比iframe的contentWindow屬性、執行window.open返回的窗口對象、或者是命名過或數值索引的window.frames。 * @param {[string]} targetOrigin 將要發送到其餘 window的數據。它將會被結構化克隆算法序列化。這意味着你能夠不受什麼限制的將數據對象安全的傳送給目標窗口而無需本身序列化 * @param {[string]} targetOrigin */
      otherWindow.postMessage( message, targetOrigin, [ transfer ] )
    複製代碼
  • 代碼api

    /** * A頁面經過postMessage方法發送消息: */
     window.onload = function() {  
         var ifr = document.getElementById('ifr');  
         var targetOrigin = "http://www.google.com";  
         ifr.contentWindow.postMessage('hello world!', targetOrigin);  
     }
     
     /** * B頁面經過message事件監聽並接受消息: * 同理也能夠B頁面發送消息而後A頁面監聽並接受消息。 */
     var onmessage = function (event) {  
         var data = event.data;//消息 
         var origin = event.origin;//消息來源地址 
         var source = event.source;//源Window對象 
         if(origin=="http://www.baidu.com"){  
             console.log(data);//hello world! 
         }  
     } 
     if (typeof window.addEventListener != 'undefined') {  
         window.addEventListener('message', onmessage, false);  
     } else if (typeof window.attachEvent != 'undefined') {  
         //for ie 
         window.attachEvent('onmessage', onmessage);  
     }
    複製代碼

3. 經過CORS跨域

  • 概念

    CORS(Cross-Origin Resource Sharing)跨域資源共享,定義了必須在訪問跨域資源時,瀏覽器與服務器應該如何溝通。CORS背後的基本思想就是使用自定義的HTTP頭部(Access-Control-Allow-Origin)讓瀏覽器與服務器進行溝通,從而決定請求或響應是應該成功仍是失敗。目前,全部瀏覽器都支持該功能,IE瀏覽器不能低於IE10。整個CORS通訊過程,都是瀏覽器自動完成,不須要用戶參與。對於開發者來講,CORS通訊與同源的AJAX通訊沒有差異,代碼徹底同樣。瀏覽器一旦發現AJAX請求跨源,就會自動添加一些附加的頭信息,有時還會多出一次附加的請求,但用戶不會有感受。所以,實現CORS通訊的關鍵是服務器。只要服務器實現了CORS接口,就能夠跨源通訊。

  • 兼容狀況

    在標準瀏覽器下XMLHttpRequst已是最新版本了,它不推薦使用onreadystatechange這個事件來監聽,推薦使用onload,另外IE若是想要實現跨域則須要使用XDomainRequest對象去實現

  • XMLHttpRequst特色

    • 能夠訪問對象的status和statusText屬性並且還支持同步請求。
    • 不能使用setReruestHeader設置自定義頭部。
    • 不能發送和接受cookie
    • 調用getAllResponseHeaders()方法總會返回空字符串
  • XDomainReques特色

    • cookie不會隨請求發送,也不會隨響應返回。
    • 只能設置請求頭部中信息中的Content-Type字段。
    • 不能訪問響應頭部信息。
    • 只支持GET和POST請求
    • XDomainReques對象的open()只支持兩個參數請求的類型和URL,全部的XDR默認都是異步請求
  • 代碼

    /** * XDomainReques * 後端須要設置頭部信息Access-Control-Allow-Origin:https://www.baidu.com */
      let xdr = new XDomainReques()
      xdr.onload = function(){
          alert(xdr.resposeText)
      }
      xdr.open('get', 'https://www.baidu.com/page')
      xdr.contentType = 'application/x-www-form-urlencoded'
      xdr.send()
      
    /** * XMLHttpRequst * 後端須要設置頭部信息Access-Control-Allow-Origin:https://www.baidu.com */
      let xhr = new XMLHttpRequest()
      xhr.onreadystatechange = function() {
          if (xhr.readyState == 4) {
              if (xhr.status >= 200 && xhr.status < 300 || xhr.status == 304){
                  alert(xhr.responseText);
              }
          }
      }
      xhr.open('get', 'https://www.baidu.com/page', true)
      xhr.send(null)
    複製代碼

4. 經過JSONP跨域

  • 原理

    經過script標籤引入的js是不受同源策略的限制的。動態的建立過script標籤引入一個js或者是一個其餘後綴形式(如php,jsp等)的文件,此文件返回一個js函數的調用,來實現跨域。

  • 代碼

    // 簡單demo
    <!DOCTYPE html>
        <html>
        <head>
            <meta charset="utf-8">
            <title></title>
            <script> function show(json) { alert(json.s) } </script>
            <script src="https://sp0.baidu.com/5a1Fazu8AA54nxGko9WTAnF6hhy/su?wd=acc&cb=show" charset="utf-8"> </script>
        </head>
    </html>
    複製代碼
    /** * jsonp 原聲音封裝 */ 
    function jsonp(obj) {
        //寫入url地址中的函數名稱,動態建立
        var callbackName = 'jsonp_callback_' + Date.now() + Math.random().toString().substr(2, 5);
        
        //建立一個script標籤
        var scriptObj = document.createElement("script");
            
        obj.parames = obj.parames || '';
        if (typeof obj.parames == 'object') {
            var arr = new Array();
            for (var key in obj.parames) {
                arr.push(key + '=' + obj.parames[key])
            }
            obj.parames = arr.join('&');
        }
        //設置標籤的請求路徑
        //像這樣:http://localhost:3000/api?callback=jsonp_callback_153498520409635392&id=1
        scriptObj.src = obj.url + '?' + 'callback=' + callbackName + '&' + obj.parames;
    
        //將建立好的帶請求的script標籤添加到html的body中
        document.body.appendChild(scriptObj);
    
        //這裏必須這樣寫window[callbackName];
        //若是寫window.callbackName會報錯沒有定義
        window[callbackName] = function (res) {
            obj.success(res);
            //刪除的時候能夠這樣寫
            //因爲請求一次會建立一個script標籤和一個函數,因此每次獲取到結果後就刪除
            delete window.callbackName;
            document.body.removeChild(scriptObj);
        }
    }
    
    /** * 調用 */
    jsonp({
        //請求地址
        url:'http://localhost:3000/api',
        //請求參數,對象
        parames:{
            id: 1
        },
        //成功回調
        success:function (res) {
            console.log(res)
        }
    })
    複製代碼
相關文章
相關標籤/搜索