跨域的理解與實現
2010-05-25
域(Domain)是Windows網絡中獨立運行的單位,域之間相互訪問則需要建立信任關系(即Trust Relation)。信任關系是連接在域與域之間的橋梁。當一個域與其他域建立了信任關系后,2個域之間不但可以按需要相互進行管理,還可以跨網分配文件和打印機等設備資源,使不同的域之間實現網絡資源的共享與管理。 有一種簡明的說法來解釋廣域跨域:跨域訪問,簡單來說就是 A 網站的 javascript 代碼試圖訪問 B 網站,包括提交內容和獲取內容。由于安全原因,跨域訪問是被各大瀏覽器所默認禁止的。
在廣域網環境中,由于瀏覽器的安全限制,網絡連接的跨域訪問時不被允許的,XmlHttpRequest也不例外。但有時候跨域訪問資源是必需的。
同源策略阻止從一個域上加載的腳本獲取或操作另一個域上的文檔屬性。也就是說,受到請求的 URL 的域必須與當前 Web 頁面的域相同。這意味著瀏覽器隔離來自不同源的內容,以防止它們之間的操作。同源策略不阻止將動態腳本元素插入文檔中。
參考理論一:在瀏覽器中不能直接來跨域訪問,而在服務器端沒有跨域安全限制。
這樣的話,可以在服務端完成跨域訪問,而在客戶端來取得結果就可以了。
參考理論二:同源策略不阻止動態腳本元素插入,腳本訪問可以跨域。
用服務器端的XmlHttpRequest代理實現跨域訪問
我們不能在瀏覽器端直接使用AJAX來跨域訪問資源,但是在服務器端是沒有這種跨域安全限制的。所以,我們只需要讓服務器端幫我們完成“跨域訪問”的工作,然后在瀏覽器端用AJAX獲取服務器端“跨域訪問”的結果就可以了。這就是所謂的在服務器端創建一個XmlHttpRequest代理,通過這個代理來訪問其他域名下的資源。這里引用Yahoo! JavaScript Developer Center上的幾張圖來進一步說明這個方案:
使用XmlHttpRequest訪問同一域名下的資源:
使用XmlHttpRequest跨域訪問資源:
用服務器端的XmlHttpRequest代理來跨域訪問資源:
編寫服務器端XmlHttpRequest代理的具體過程就不贅述了,無非是創建一個自定義的HTTP請求。
用動態script標簽實現客戶端的跨域訪問
很明顯,上一個方案必須要在服務器端做相應的改動才能實現跨域訪問。但是有些時候,用戶不能改動服務器端的源代碼,而且更多的時候,用戶并沒有Web開發的基礎,他們只會簡單的復制、粘貼操作。這個時候,第一個方案就不能滿足一個服務提供者的需求了。
我們應該能注意到,雖然瀏覽器有跨域訪問的限制,但是我們是可以通過script標簽遠程引用其他域名下的腳本文件的。而且,script標簽的 src屬性不一定必須是一個存在的js文件,也可以是一個http handler的url,只要這個http handler返回的是一個text/javascript類型的響應就可以了。
這樣,我們的第二個方案就浮出水面了。只要讓用戶添加一個script標簽,這個script標簽的src屬性指向我們api的url,并提供 api需要的一些參數,通常其中包括了一個作為callback的js函數名。針對這個腳本請求,我們服務器端的http handler會根據url中攜帶的參數,生成并返回相應的腳本,通常這個腳本的內容是調用callback函數,并傳入用戶需要的數據作為參數。于是,一個跨域訪問的過程就完成了。下面是一個Yahoo! JavaScript Developer Center上提供的一個例子:
01
|
<html>
|
02
|
<head>
|
03
|
<title>How Many Pictures Of Madonna Do We Have?</title>
|
04
|
<script type=
"text/javascript"
>
|
05
|
function
ws_results(obj)
|
06
|
{
|
07
|
alert(obj.ResultSet.totalResultsAvailable);
|
08
|
}
|
09
|
</script>
|
10
|
<script type=
"text/javascript"
src=
"
http://search.yahooapis.com/ImageSearchService/V1/imageSearch?appid=YahooDemo&query=Madonna&output=json&callback=ws_results
"
></script>
|
11
|
</head>
|
12
|
<body>
|
13
|
</body>
|
14
|
</html>
|
將這段代碼復制到文本編輯器中,另存為xxx.html,再將另存到文件拖到瀏覽器中,就能看到效果了(網速慢的話需要等待一下)。在這個例子中,api的url的callback參數指定了跨域訪問成功后會調用的函數的名稱,這個函數有一個參數,這個參數就是用戶跨域訪問需要的數據,通常是一個json對象。
這個例子有一個不足之處,就是這個跨域訪問實際上是“靜態的”,也就是說不能隨時動態的實現跨域訪問,例如同一個按鈕點擊事件來觸發跨域訪問。其實只要將代碼稍微修改一下,就可以實現動態的跨域訪問了,下面是修改后的代碼:
01
|
<html>
|
02
|
<head>
|
03
|
<title>How Many Pictures Of Madonna Do We Have?</title>
|
04
|
<script type=
"text/javascript"
>
|
05
|
function
ws_results(obj)
|
06
|
{
|
07
|
alert(obj.ResultSet.totalResultsAvailable);
|
08
|
}
|
09
|
|
10
|
function
onClick()
|
11
|
{
|
12
|
var
script = document.createElement(
"script"
);
|
13
|
script.type =
"text/javascript"
;
|
14
|
script.src =
"
http://search.yahooapis.com/ImageSearchService/V1/imageSearch?appid=YahooDemo&query=Madonna&output=json&callback=ws_results
"
;
|
15
|
document.body.appendChild(script);
|
16
|
}
|
17
|
</script>
|
18
|
</head>
|
19
|
<body>
|
20
|
<input type=
"button"
value=
"click me!"
onclick=
"onClick()"
>
|
21
|
</body>
|
22
|
</html>
|
服務器端的http handler處理請求的過程這里也不贅述了,關鍵就是根據用戶的參數生成js代碼。
其他實現跨域訪問的方案
除了上面兩種方案之外,還有其他一下方法同樣可以實現跨域訪問,例如:
- apache server的mod_rewrite模塊或者mod_proxy模塊可以幫你自動實現服務器端的XmlHttpRequest代理,你可以像調用當前域名下的資源一樣的調用其他域名下的資源。
- 據說動態生成iframe也可以實現跨域調用,沒有仔細研究過。
Javascript跨域和Ajax跨域解決方案
Ajax跨域和JS的跨域通信(Cross The Site)的幾種解決方案
最近做的一個項目中需要ajax跨域取得數據,如果是在本域中確實沒有問題,但是放到二級域和其他域下瀏覽器直接就彈出提示框:“該頁正在訪問其控制范圍之外的數據,這有些危險,是否繼續"。
什么引起了ajax跨域不能的問題?ajax本身實際上是通過XMLHttpRequest對象來進行數據的交互,而瀏覽器出于安全考慮,不允許js代碼進行跨域操作,所以會警告。
有什么完美的解決方案么?沒有。解決方案有不少,但是只能是根據自己的實際情況來選擇。
具體情況有:
- 本域和子域的相互訪問: www.aa.com和book.aa.com
- 本域和其他域的相互訪問: www.aa.com和www.bb.com 用 iframe
- 本域和其他域的相互訪問: www.aa.com和www.bb.com 用 XMLHttpRequest訪問代理
- 本域和其他域的相互訪問: www.aa.com和www.bb.com 用 JS創建動態腳本
解決方法:
- 如果想做到數據的交互,那么www.aa.com和book.aa.com必須由你來開發才可以。可以將book.aa.com用iframe添加到 www.aa.com的某個頁面下,在www.aa.com和iframe里面都加上document.domain = "aa.com",這樣就可以統一域了,可以實現跨域訪問。就和平時同一個域中鑲嵌iframe一樣,直接調用里面的JS就可以了。
- 當兩個域不同時,如果想相互調用,那么同樣需要兩個域都是由你來開發才可以。用iframe可以實現數據的互相調用。解決方案就是用window.location對象的hash屬性。hash屬性就是http://domian/web/a.htm#dshakjdhsjka 里面的#dshakjdhsjka。利用JS改變hash值網頁不會刷新,可以這樣實現通過JS訪問hash值來做到通信。不過除了IE之外其他大部分瀏覽器只要改變hash就會記錄歷史,你在前進和后退時就需要處理,非常麻煩。不過再做簡單的處理時還是可以用的。大體的過程是頁面a和頁面b在不同域下,b通過iframe添加到a里,a通過JS修改iframe的hash值,b里面做一個監聽(因為JS只能修改hash,數據是否改變只能由b自己來判斷),檢測到b的hash值被修改了,得到修改的值,經過處理返回a需要的值,再來修改a的hash值(這個地方要注意,如果a 本身是那種查詢頁面的話比如http://domian/web/a.aspx?id=3,在b中直接parent.window.location是無法取得數據的,同樣報沒有權限的錯誤,需要a把這個傳過來,所以也比較麻煩),同樣a里面也要做監聽,如果hash變化的話就取得返回的數據,再做相應的處理。
- 這種情形是最經常遇到的,也是用的最多的了。就是www.aa.com和www.bb.com你只能修改一個,也就是另外一個是別人的,人家告訴你你要取得數據就訪問某某連接參數是什么樣子的,最后返回數據是什么格式的。而你需要做的就是在你的域下新建一個網頁,讓服務器去別人的網站上取得數據,再返回給你。domain1下的a向同域下的GetData.aspx請求數據,GetData.aspx向domain2下的 ResponseData.aspx 發送請求,ResponseData.aspx 返回數據給 GetData.aspx,GetData.aspx 再返回給 a,這樣就完成了一次數據請求。GetData.aspx在其中充當了代理的作用。
- 這個和上個的區別就是請求是使用<script>標簽來請求的,這個要求也是兩個域都是由你來開發才行。原理就是JS文件注入,在本域內的a 內生成一個JS標簽,它的SRC指向請求的另外一個域的某個頁面b,b返回數據即可,可以直接返回JS的代碼。因為script的src屬性是可以跨域的。
總結:
- 第一種情況:域和子域的問題,可以完全解決交互。
- 第二種情況:跨域,實現過程非常麻煩,需要兩個域開發者都能控制,適用于簡單交互。
- 第三種情況:跨域,開發者只控制一個域即可,實現過程需要增加代理取得數據,是常用的方式。
- 第四種情況:跨域,兩個域開發者都需要控制,返回一段js代碼。
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
