常見的跨域解決方案

1、JSONP

受瀏覽器同源策略的限制,網頁無法向其他域發送ajax請求,但在頁面中引入其他域的腳本是可以的,最常見的例子就是在頁面中引入cdn服務器上的js文件及圖片等靜態資源,JSONP正是利用這一特性實現了跨域。
例如,http://test.com/test.html獲取http://abc.test.com/data.php上的數據,那么只需要在test.html中加入以下代碼即可:

<script>
    function handleData(data){
        // do something
    }
</script>
<script scr="http://abc.test.com/data.php?callback=handleData"></script>

其實第二個標簽返回的是一個可執行的js文件,data.php包含這樣一段代碼:

<?php
$callback = $_GET['callback']; 
$data = array(1,2);
echo $callback.'('.json_encode($data)')'
?>

當第二個標簽加載完畢后,就會執行handleData([1,2])

2、document.domain

瀏覽器同源策略中第二個限制是不同域的框架之間不能進行js交互(但可獲得彼此的window對象)
例如,在http://abc.test.com/test.html這個頁面中有一個iframe起src為http://bcd.test.com/test2.html,以下代碼會出現注釋中的問題

//test.html
<script>
    function ifrLoad(){
        var iframe=document.getElementById('iframe');
        var ifrWin=iframe.contentWindow; //獲得iframe的window對象(但屬性和方法幾乎不可用)
        console.log(ifrWin.document); //無法獲得document屬性
    }
</script>
<iframe src="http://bcd.test.com/test2.html" id="iframe" onload="ifrLoad()" ></iframe>

此時可通過document.domain設置當前文檔的原始域實現跨域操作,只要兩個頁面的原始域相同,就可通過js獲得iframe中的各種屬性和方法了.

<!-- test.html   -->
<script>
   document.domain='test.com'
   function ifrLoad(){
       var iframe=document.getElementById('iframe');
       var ifrWin=iframe.contentWindow; //獲得iframe的window對象(但屬性和方法幾乎不可用)
       console.log(ifrWin.document);  //正常
   }
</script>
<iframe src="https:www.baidu.com" id="iframe" onload="ifrLoad()" ></iframe>
<!-- test2.html -->
<script>
    document.domain='test.com'
</script>

需要要注意的是,d只能把document.domain設置成自身或更高一級的父域,且主域必須相同。例如:a.b.test.com 中某個文檔的document.domain 可以設成a.b.test.com、b.test.com 、test.com中的任意一個,但是不可以設成 c.a.b.test.com,因為這是當前域的子域,也不可以設成example.com,因為主域已經不相同了。

3 、window.name

window.name屬性在不同的頁面(甚至不同域名)加載后依舊存在(如果沒修改則值不會變化),并且可以支持非常長的 name 值(2MB)假設我們在百度首頁設置了window.name為'baidu',

我們在同一窗口地址欄中輸入谷歌網址,此時我們查看window.name會發現window.name依然為‘baidu’

此時我們修改window.name為'google',再次打開百度,發現window.name變為了‘google’


了解了window.name屬性之后,我們想要從test頁面中獲得test1中的數據就容易多了,可以首先加載test1頁面,將test頁面需要的數據存在window.name中,然后加載test頁面,但貌似這種方式很蠢,解決辦法是在test頁面中設置一個隱藏的iframe標簽用于獲取test2頁面的window.name,但需要注意的是受同源策略的限制,需要在iframe中的test2加載完畢后,在iframe中加載與test同源的test3頁面。

<!-- http://abc.test.com/test.html   -->
<script>
    function loadData(){
        var iframe=document.getElementById('iframe');
        iframe.onload=fucntion(){
            var data=iframe.contentWindow.name
            console.log(data)
        }
        iframe.src='http://abc.test.com/test3.html'

    }
</script>
<iframe src="http://bcd.test.com/test2.html" id="iframe" onload="loadData()" ></iframe>

4、window.postMessage

window.postMessage是HTML5的API,允許跨域在兩個窗口/frames之間發送數據。需要注意的是調用postMessage方法的window對象是指接受消息的那個window對象。其調用方式如下:

otherWindow.postMessage(message, targetOrigin, [transfer]);

otherWindow 是其他窗口的一個引用,比如iframe的contentWindow屬性、執行window.open返回的窗口對象、或者是命名過或數值索引的window.frames。
argetOrigin 是用來接收消息的那個window對象所在的域,如果不希望做限定,可以用*代替。
transfer 可選,是一串和message 同時傳遞的 Transferable 對象. 這些對象的所有權將被轉移給消息的接收方,而發送一方將不再保有所有權。

<!-- http://abc.test.com/test.html   -->
<script>
    function ifrLoad(){
        var iframe=document.geElementById('iframe');
        iframe.contentWindow.postMessage('hello world!','*')
    }
</script>
<iframe src="http://bcd.test.com/test2.html" id="iframe" onload="ifrLoad()" '></iframe>
<!-- test2.html -->
<script>
    window.onmessage=function(e){
        console.log(e.data)
    }
</script>

5 、跨域資源共享(CORS)

服務器設置Access-Control-Allow-OriginHTTP響應頭之后,瀏覽器將會允許跨域請求,但需要瀏覽器需要支持HTML5,可以支持POST,PUT等方法

HTML5標準中提出的跨域資源共享(Cross Origin Resource Share,CORS)支持其他的HTTP方法如PUT, POST等,可以從本質上解決跨域問題。
例如,從http://a.com要訪問http://b.com的數據,通常情況下Chrome會因跨域請求而報錯:

XMLHttpRequest cannot load http://b.com. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://a.com' is therefore not allowed access.

錯誤原因是被請求資源沒有設置Access-Control-Allow-Origin,所以我們在b.com的服務器中設置這個響應頭字段即可:

Access-Control-Allow-Origin: * # 允許所有域名訪問,或者
Access-Control-Allow-Origin: http://a.com # 只允許所有域名訪問

為 xhr設置 withCredentials后CORS方法跨域還可攜帶Cookie,但 PUT/POST 請求需要注意處理 preflight 請求

6、Proxy

可以在服務器端設置一個代理,由服務器端向跨域下的網站發出請求,再將請求結果返回給前端,成功避免同源策略的限制。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 什么是同源策略 同源策略是指瀏覽器處于安全方面的考慮只允許本域下的接口交互。不同源的客戶端腳本在沒有明確授權的情況...
    夜流光丶閱讀 280評論 0 0
  • 什么是跨域? 2.) 資源嵌入:、、、等dom標簽,還有樣式中background:url()、@font-fac...
    電影里的夢i閱讀 2,389評論 0 5
  • 1. 什么是跨域? 跨域一詞從字面意思看,就是跨域名嘛,但實際上跨域的范圍絕對不止那么狹隘。具體概念如下:只要協議...
    w_zhuan閱讀 532評論 0 0
  • 1. 什么是跨域? 跨域一詞從字面意思看,就是跨域名嘛,但實際上跨域的范圍絕對不止那么狹隘。具體概念如下:只要協議...
    他在發呆閱讀 829評論 0 0
  • 打嗝其實是人類進化的產物,也是身體的保護反應。 在胸腔和腹腔之間,有一個肌肉膜叫膈肌,厚厚的、像帽子,把胸腔和腹腔...
    美妙的養生閱讀 346評論 0 0