這一策略對于JavaScript代碼能夠訪問的頁面內(nèi)容做了很重要的限制,即JavaScript只能訪問與包含它的文檔在同一域下的內(nèi)容。
JavaScript這個安全策略在進行多iframe或多窗口編程、以及Ajax編程時顯得尤為重要。根據(jù)這個策略,在baidu.com下的頁面中包含的JavaScript代碼,不能訪問在google.com域名下的頁面內(nèi)容;甚至不同的子域名之間的頁面也不能通過JavaScript代碼互相訪問。對于Ajax的影響在于,通過XMLHttpRequest實現(xiàn)的Ajax請求,不能向不同的域提交請求,例如,在abc.example.com下的頁面,不能向def.example.com提交Ajax請求,等等。
然而,當進行一些比較深入的前端編程的時候,不可避免地需要進行跨域操作,這時候“同源策略”就顯得過于苛刻。本文就這個問題,概括了跨域所需要的一些技術(shù)。
下面我們分兩種情況討論跨域技術(shù):首先討論不同子域的跨域技術(shù),然后討論完全不同域的跨域技術(shù)。
(一)不同子域的跨域技術(shù)。
我們分兩個問題來分別討論:第一個問題是如何跨不同子域進行JavaScript調(diào)用;第二個問題是如何向不同子域提交Ajax請求。
先來解決第一個問題,假設(shè)example.com域下有兩個不同子域:abc.example.com和def.example.com。現(xiàn)在假設(shè)在def.example.com下面有一個頁面,里面定義了一個JavaScript函數(shù):
這樣,兩個頁面就變?yōu)橥蛄耍懊娴恼{(diào)用也可以正常執(zhí)行了。
這里需要注意的一點是,一個頁面的document.domain屬性只能設(shè)置成一個更頂級的域名(除了一級域名),但不能設(shè)置成比當前域名更深層的子域名。例如,abc.example.com的頁面只能將它的domain設(shè)置成example.com,不能設(shè)置成sub.abc.example.com,當然也不能設(shè)置成一級域名com。
上面的例子討論的是兩個頁面屬于iframe嵌套關(guān)系的情況,當兩個頁面是打開與被打開的關(guān)系時,原理也完全一樣。
下面我們來解決第二個問題:如何向不同子域提交Ajax請求。
通常情況下,我們會用與下面類似的代碼來創(chuàng)建一個XMLHttpRequest對象:
假設(shè)上面的代碼包含在一個abc.example.com域名下的頁面里,則這個GET請求可以正常發(fā)送成功,沒有任何問題。然而,如果現(xiàn)在要向def.example.com發(fā)送請求,則出現(xiàn)跨域問題,JavaScript引擎拋出異常。
解決的辦法是,在def.example.com域下放置一個跨域文件,假設(shè)叫crossdomain.html;然后將前面的newRequest函數(shù)的定義移到這個跨域文件中;最后像之前修改document.domain值的做法一樣,在crossdomain.html文件和abc.example.com域下調(diào)用Ajax的頁面頂端,都加上:
(二)完全不同域的跨域技術(shù)。
如果頂級域名都不相同,例如example1.com和example2.com之間想通過JavaScript在前端通信,則所需要的技術(shù)更復(fù)雜些。
在講解不同域的跨域技術(shù)之前,我們首先明確一點,下面要講的技術(shù)也同樣適用于前面跨不同子域的情況,因為跨不同子域只是跨域問題的一個特例。當然,在恰當?shù)那闆r下使用恰當?shù)募夹g(shù),能夠保證更優(yōu)的效率和更高的穩(wěn)定性。
簡言之,根據(jù)不同的跨域需求,跨域技術(shù)可以歸為下面幾類:
1、JSONP跨域GET請求
2、通過iframe實現(xiàn)跨域
3、flash跨域HTTP請求
4、window.postMessage
下面詳細介紹各種技術(shù)。
1. JSONP。
利用在頁面中創(chuàng)建<script>節(jié)點的方法向不同域提交HTTP請求的方法稱為JSONP,這項技術(shù)可以解決跨域提交Ajax請求的問題。JSONP的工作原理如下所述:
假設(shè)在http://example1.com/index.php這個頁面中向http://example2.com/getinfo.php提交GET請求,我們可以將下面的JavaScript代碼放在http://example1.com/index.php這個頁面中來實現(xiàn):
當GET請求從http://example2.com/getinfo.php返回時,可以返回一段JavaScript代碼,這段代碼會自動執(zhí)行,可以用來負責調(diào)用http://example1.com/index.php頁面中的一個callback函數(shù)。
JSONP的優(yōu)點是:它不像XMLHttpRequest對象實現(xiàn)的Ajax請求那樣受到同源策略的限制;它的兼容性更好,在更加古老的瀏覽器中都可以運行,不需要XMLHttpRequest或ActiveX的支持;并且在請求完畢后可以通過調(diào)用callback的方式回傳結(jié)果。
JSONP的缺點則是:它只支持GET請求而不支持POST等其它類型的HTTP請求;它只支持跨域HTTP請求這種情況,不能解決不同域的兩個頁面之間如何進行JavaScript調(diào)用的問題。
2. 通過iframe實現(xiàn)跨域。
iframe跨域的方式,功能強于JSONP,它不僅能用來跨域完成HTTP請求,還能在前端跨域?qū)崿F(xiàn)JavaScript調(diào)用。因此,完全不同域的跨域問題,通常采用iframe的方式來解決。
與JSONP技術(shù)通過創(chuàng)建<script>節(jié)點向不同的域提交GET請求的工作方式類似,我們也可以通過在http://example1.com/index.php頁面中創(chuàng)建指向http://example2.com/getinfo.php的iframe節(jié)點跨域提交GET請求。然而,請求返回的結(jié)果無法回調(diào)http://example1.com/index.php頁面中的callback函數(shù),因為受到“同源策略”的影響。
為了解決這個問題,我們需要在example1.com下放置一個跨域文件,比如路徑是http://example1.com/crossdomain.html。
當http://example2.com/getinfo.php這個請求返回結(jié)果的時候,它大體上有兩個選擇。
第一個選擇是,它可以在iframe中做一個302跳轉(zhuǎn),跳轉(zhuǎn)到跨域文件http://example1.com/crossdomain.html,同時將返回結(jié)果經(jīng)過URL編碼之后作為參數(shù)綴在跨域文件URL后面,例如http://example1.com/crossdomain.html?result=<URL-Encoding-Content>。
另一個選擇是,它可以在返回的頁面中再嵌入一個iframe,指向跨域文件,同時也是將返回結(jié)果經(jīng)過URL編碼之后作為參數(shù)綴在跨域文件URL后面。
在跨域文件中,包含一段JavaScript代碼,這段代碼完成的功能,是從URL中提取結(jié)果參數(shù),經(jīng)過一定處理后調(diào)用原來的http://example1.com/index.php頁面中的一個預(yù)先約定好的callback函數(shù),同時將結(jié)果參數(shù)傳給這個函數(shù)。http://example1.com/index.php頁面和跨域文件是在同一個域下的,因此這個函數(shù)調(diào)用可以通過。跨域文件所在iframe和原來的http://example1.com/index.php頁面的關(guān)系,在前述第一種選擇下,后者是前者的父窗口,在第二種選擇下,后者是前者的父窗口的父窗口。
根據(jù)前面的敘述,有了跨域文件之后,我們就可以實現(xiàn)通過iframe方式在不同域之間進行JavaScript調(diào)用。這個調(diào)用過程可以完全跟HTTP請求無關(guān),例如有些站點可以支持動態(tài)地調(diào)整在頁面中嵌入的第三方iframe的高度,這其實是通過在第三方iframe里面檢測自己頁面的高度變化,然后通過跨域方式的函數(shù)調(diào)用將這個變化告知父窗口來完成的。
既然利用iframe可以實現(xiàn)跨域JavaScript調(diào)用,那么跨域提交POST請求等其它類型的HTTP請求就不是難事。例如我們可以跨域調(diào)用目標域的JavaScript代碼在目標域下提交Ajax請求(GET/POST/etc.),然后將返回的結(jié)果再跨域傳原來的域。
使用iframe跨域,優(yōu)點是功能強大,支持各種瀏覽器,幾乎可以完成任何跨域想做的事情;缺點是實現(xiàn)復(fù)雜,要處理很多瀏覽器兼容問題,并且傳輸?shù)臄?shù)據(jù)不宜過大,過大了可能會超過瀏覽器對URL長度的限制,要考慮對數(shù)據(jù)進行分段傳輸?shù)取?/P>
3. 利用flash實現(xiàn)跨域HTTP請求
據(jù)稱,flash在瀏覽器中的普及率高達90%以上。
flash代碼和JavaScript代碼之間可以互相調(diào)用,并且flash的“安全沙箱”機制與JavaScript的安全機制并不盡相同,因此,我們可以利用flash來實現(xiàn)跨域提交HTTP請求(支持GET/POST等)。
例如,我們用瀏覽器訪問http://example1.com/index.php這個頁面,在這個頁面中引用了http://example2.com/flash.swf這個flash文件,然后在flash代碼中向http://example3.com/webservice.php發(fā)送HTTP請求。
這個請求能否被成功發(fā)送,取決于在example3.com的根路徑下是否放置了一個crossdomain.xml以及這個crossdomain.xml的配置如何。flash的“安全沙箱”會保證:僅當example3.com服務(wù)器在根路徑下確實放置了crossdomain.xml文件并且在這個文件中配置了允許接受來自example2.com的flash的請求時,這個請求才能真正成功。下面是一個crossdomain.xml文件內(nèi)容的例子:
4. window.postMessage
window.postMessage是HTML標準的下一個版本HTML5支持的一個新特性。受當前互聯(lián)網(wǎng)技術(shù)突飛猛進的影響,瀏覽器跨域通信的需求越來越強烈,HTML標準終于把跨域通信考慮進去了。但目前HTML5仍然只是一個draft。
window.postMessage是一個安全的實現(xiàn)直接跨域通信的方法。但是目前并不是所有瀏覽器都能支持,只有Firefox 3、Safari 4和IE8可以支持這個調(diào)用。
使用它向其它窗口發(fā)送消息的調(diào)用方式大概如下:
用iframe內(nèi)嵌其它域下的頁面:
注:postMessage方式正以意想不到的速度得到各種新瀏覽器的支持,應(yīng)予以著重考慮。
新聞熱點
疑難解答