也談?wù)勍床呗院涂缬騿栴}

跨域問題是個老生常談的問題,在WEB開發(fā)中會經(jīng)常聽到,但是很多時候的開發(fā)并不涉及跨域問題什么的,所以一般也不會去深究其中的一些細(xì)節(jié),這篇文章就來總結(jié)一下瀏覽器的同源策略和跨域問題。

1 同源策略

所謂同源策略,指的是瀏覽器對不同源的腳本或者文本的訪問方式進(jìn)行的限制。比如源a的js不能讀取或設(shè)置引入的源b的元素屬性。

那么先定義下什么是同源,所謂同源,就是指兩個頁面具有相同的協(xié)議,主機(jī)(也常說域名),端口,三個要素缺一不可。

可以看下面的幾個示例來更加清楚的了解一下同源的概念:

URL1 | URL2 | 說明 | 是否允許通信
------------ | ------------- | ------------
http://www.foo.com/js/a.js | http://www.foo.com/js/b.js | 協(xié)議、域名、端口都相同 | 允許
http://www.foo.com/js/a.js | http://www.foo.com:8888/js/b.js| 協(xié)議、域名相同,端口不同 | 不允許
https://www.foo.com/js/a.js | http://www.foo.com/js/b.js| 主機(jī)、域名相同,協(xié)議不同 | 不允許
http://www.foo.com/js/a.js | http://www.bar.com/js/b.js| 協(xié)議、端口相同,域名不同 | 不允許
http://www.foo.com/js/a.js | http://foo.com/js/b.js| 協(xié)議、端口相同,主域名相同,子域名不同 | 不允許

同源策略限制了不同源之間的交互,但是有人也許會有疑問,我們以前在寫代碼的時候也常常會引用其他域名的js文件,樣式文件,圖片文件什么的,沒看到限制啊,這個定義是不是錯了。其實(shí)不然,同源策略限制的不同源之間的交互主要針對的是js中的XMLHttpRequest等請求,下面這些情況是完全不受同源策略限制的。

  • 頁面中的鏈接,重定向以及表單提交是不會受到同源策略限制的。鏈接就不用說了,導(dǎo)航網(wǎng)站上的鏈接都是鏈接到其他站點(diǎn)的。而你在域名www.foo.com下面提交一個表單到www.bar.com是完全可以的。
  • 跨域資源嵌入是允許的,當(dāng)然,瀏覽器限制了Javascript不能讀寫加載的內(nèi)容。如前面提到的嵌入的<script src="..."></script>,<img>,<link>,<iframe>等。當(dāng)然,如果要阻止iframe嵌入我們網(wǎng)站的資源(頁面或者js等),我們可以在web服務(wù)器加上一個X-Frame-Options DENY頭部來限制。nginx就可以這樣設(shè)置add_header X-Frame-Options DENY;。

既然有這么多的情況是沒有同源策略限制的,那么通常的跨域問題從何而來呢?轉(zhuǎn)到下一節(jié)跨域問題。

2 跨域問題

這一節(jié)來討論下跨域問題,當(dāng)然前置條件是我們在WEB服務(wù)器或者服務(wù)端腳本中設(shè)置ACCESS-CONTROL-ALLOW-ORIGIN頭部,如果設(shè)置了這些頭部并允許某些域名跨域訪問,則瀏覽器就會跳過同源策略的限制返回對應(yīng)的內(nèi)容。此外,如果你不是通過瀏覽器去獲取資源,比如你通過一個python腳本去調(diào)用接口或者獲取js文件,也不在這個限制之內(nèi)。

2.1 Ajax跨域

通過ajax調(diào)用其他域的接口會有跨域問題。比如下面的例子,我在http://www.foo.com/index.html中通過ajax調(diào)用請求http://www.bar.com/js/test.js頁面,此時是會報錯的。

XMLHttpRequest cannot load http://www.bar.com/js/test.js. 
No 'Access-Control-Allow-Origin' header is present on the requested resource. 
Origin 'http://www.foo.com' is therefore not allowed access.

這是因?yàn)槲覀兊腤EB服務(wù)器沒有設(shè)置ACCESS-CONTROL-ALLOW-ORIGIN頭部,默認(rèn)情況下是禁止跨域引用資源的。當(dāng)然這里有一點(diǎn)要注意,其實(shí)這個跨域請求是發(fā)送成功了的,服務(wù)器也有返回test.js內(nèi)容,只是瀏覽器禁止Javascript去取response的數(shù)據(jù)而已。如果要設(shè)置ACCESS-CONTROL-ALLOW-ORIGIN頭部,nginx可以使用下面的代碼

add_header 'Access-Control-Allow-Origin' 'http://www.foo.com';
add_header 'Access-Control-Allow-Credentials' 'true';
add_header 'Access-Control-Allow-Methods' 'GET,POST';

另外,我們看到直接通過Javascript去取iframe中的元素也是會報錯的,因?yàn)橛蛎煌?。報錯如下所示

Uncaught SecurityError: Failed to read the 'contentDocument' property from 'HTMLIFrameElement': Blocked a frame with origin "http://www.foo.com" from accessing a frame with origin "http://foo.com". 
The frame being accessed set "document.domain" to "foo.com", but the frame requesting access did not. Both must set "document.domain" to the same value to allow access.

這時因?yàn)槲覀兊闹饔蛎嗤虼丝梢栽趇ndex.html和test.html中設(shè)置document.domain='foo.com'來訪問iframe中的元素。注意,是兩個域名下面的文件都要設(shè)置,即便是同樣的主域名。當(dāng)然這是特例,如果是兩個完全不同的域名,是沒有辦法的,你不能把www.foo.comdomain設(shè)置成www.163.com。

此外,在index.html里面也可以看到通過<script>,<iframe>等標(biāo)簽都是可以跨域內(nèi)嵌資源的。

# index.html
<!DOCTYPE html>
<html>
<head>
<title>test cross domain</title>
<script src="/js/jquery.js"></script>
<script src="http://www.bar.com/js/test.js"></script>
<script>
$(function(){
    document.domain = 'foo.com'; //1 注釋掉則會報錯 
    var ifr = document.getElementById("testframe");
    ifr.onload = function(){
        var doc = ifr.contentDocument || ifr.contentWindow.document;
        alert(doc.getElementsByTagName("h1")[0].childNodes[0].nodeValue);
    }
});

$.ajax("http://www.bar.com/js/test.js"); //2 報錯
</script>

</head>
<body>
<h1>Test Cross Domain</h1>
<iframe id="testframe" src="http://foo.com/test.html"></iframe>
</body>
</html>

當(dāng)然還可以通過iframe,location.hash,window.name,HTML5的postMessage等方法來實(shí)現(xiàn)跨域資源訪問,更多內(nèi)容參見Rain Man的這篇文章 JavaScript跨域總結(jié)和解決辦法

2.2 JSONP跨域訪問

JSONP也是開發(fā)中常見到的內(nèi)容,在jquery中就有封裝,通過ajax請求多帶上一個jsonp的參數(shù)即可。JSONP也能夠?qū)崿F(xiàn)跨域訪問資源,但是它的實(shí)現(xiàn)原理其實(shí)跟ajax沒有多少關(guān)系,它是通過動態(tài)插入<script>標(biāo)簽來實(shí)現(xiàn)跨域資源訪問的,因?yàn)楦鶕?jù)前面內(nèi)容我們已經(jīng)知道,嵌入跨域資源瀏覽器是允許的。

下面通過一個簡單的例子來說明JSONP的原理。
兩個文件,第一個是http://www.foo.com/jsonp.html,通過動態(tài)創(chuàng)建script標(biāo)簽加載了http://www.bar.com/js/outer.js文件,然后outer.js文件返回的內(nèi)容正好是一個函數(shù)調(diào)用,如此,實(shí)現(xiàn)了數(shù)據(jù)傳遞和回調(diào)過程。當(dāng)然,實(shí)際的jsonp接口中,會讓你傳一個函數(shù)名過去,然后返回的數(shù)據(jù)中回調(diào)函數(shù)名就是你傳的函數(shù)名,回調(diào)函數(shù)的參數(shù)則是封裝的json格式。jQuery中的jsonp實(shí)現(xiàn)原理基本就是這樣,更詳細(xì)的jsonp原理可以參見這篇大作 深入淺出JSONP

# jsonp.html
<script type="text/javascript">
    function callback(data) {
        alert(data.message);
    }
    function addScriptTag(src){
    var script = document.createElement('script');
        script.src = src;
        document.body.appendChild(script);
    }
    
    window.onload = function(){
        addScriptTag("http://www.foo.com/js/outer.js");
    }
</script>

# outer.js
callback({message:"success"});

參考資料

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 1. 什么是同源策略 同源是指域名、協(xié)議、端口相同。同源策略(Same-Origin Policy)是瀏覽器的一個...
    65_劉璐閱讀 782評論 0 0
  • 問答 1. 什么是同源策略 同源是指域名、協(xié)議、端口相同。同源策略(Same-Origin Policy)是瀏覽器...
    Maggie_77閱讀 535評論 0 1
  • Section1、為什么要跨域? 自古以來(1995年起),為了用戶的信息安全,瀏覽器就引入了同源策略。那么同源策...
    qhaobaba閱讀 459評論 0 0
  • 1.什么是同源策略 1.要了解同源策略,我們必須先知道源即orgin 以百度頁面為例,谷歌瀏覽器打開控制臺:輸入l...
    GarenWang閱讀 1,546評論 2 8
  • 從前你也不像她,沒有留到腰際的,染成深紅色的發(fā),一樣沒有家。 日子悄然無聲,像是一根刺從身體出發(fā),刺破鮮紅,刺破籬...
    簡背影閱讀 366評論 1 2

友情鏈接更多精彩內(nèi)容