DOM跨域

同源策略限制從一個源加載的文檔或腳本如何與來自另外一個源的資源進行交互。這是一個用於隔離潛在惡意文件的關鍵的安全機制。html

可是有時候跨域請求資源是合理的需求,本文嘗試從多篇文章中彙總至今存在的全部跨域請求解決方案。git

跨域請求github

首先須要瞭解的是同源和跨源的概念。對於相同源,其定義爲:若是協議、端口(若是指定了一個)和主機對於兩個頁面是相同的,則兩個頁面具備相同的源。只要三者之一任意一點有不一樣,那麼就爲不一樣源。後端

當一個資源從與該資源自己所在的服務器的域或端口不一樣的域或不一樣的端口請求一個資源時,資源會發起一個跨域 HTTP 請求。跨域

而有關跨域請求受到限制的緣由能夠參考以下 MDN 文檔片斷:瀏覽器

跨域不必定是瀏覽器限制了發起跨站請求,而也多是跨站請求能夠正常發起,可是返回結果被瀏覽器攔截了。安全

最好的例子是 CSRF 跨站攻擊原理,請求是發送到了後端服務器不管是否跨域!注意:有些瀏覽器不容許從 HTTPS 的域跨域訪問 HTTP,好比 Chrome 和 Firefox,這些瀏覽器在請求還未發出的時候就會攔截請求,這是一個特例。bash

解決方法彙總服務器

如下咱們由簡及深介紹各類存在的跨域請求解決方案,包括 document.domain, location.hash, window.name, window.postMessage, JSONP, WebSocket, CORS 。dom

document.domain

document.domain 的做用是用來獲取/設置當前文檔的原始域部分,例如:

// 對於文檔 www.example.xxx/good.html 
document.domain=」www.example.xxx」

// 對於URI http://developer.mozilla.org/en/docs/DOM 
document.domain=」developer.mozilla.org」 
若是當前文檔的域沒法識別,那麼 domain 屬性會返回 null。

在根域範圍內,Mozilla容許你把domain屬性的值設置爲它的上一級域。例如,在 developer.mozilla.org 域內,能夠把domain設置爲 「mozilla.org」 但不能設置爲 「mozilla.com」 或者」org」。

所以,若兩個源所用協議、端口一致,主域相同而二級域名不一樣的話,能夠借鑑該方法解決跨域請求。

好比若咱們在http://a.github.io 頁面執行如下語句:

document.domain = 「github.io」 
那麼以後頁面對 github.io 發起請求時頁面則會成功經過對 github.io 的同源檢測。

比較直接的一個操做是,當咱們在 a.github.io 頁面中利用 iframe 去加載 github.io 時,經過如上的賦值後,咱們能夠在 a.github.io 頁面中去操做 iframe 裏的內容。

咱們同時考慮另外一種狀況:存在兩個子域名 a.github.io 以及 b.github.io , 其中前者域名下網頁 a.html 經過 iframe 引入了後者域名下的 b.html,此時在 a.html 中是沒法直接操做 b.html 的內容的。

一樣利用 document.domain ,咱們在兩個頁面中均加入

document.domain=’github.io’ 這樣在以上的 a.html 中就能夠操做經過 iframe 引入的 b.html 了。

document.domain的優勢在於解決了主語相同的跨域請求,可是其缺點也是很明顯的:好比一個站點受到攻擊後,另外一個站點會所以引發安全漏洞;若一個頁面中引入多個 iframe,想要操做全部的 iframe 則須要設置相同的 domain。

location.hash

location.hash 是一個可讀可寫的字符串,該字符串是 URL 的錨部分(從 # 號開始的部分)。例如:

// 對於頁面 http://example.com:1234/test.htm#part2 
location.hash = 「#part2」 
同時,因爲咱們知道改變 hash 並不會致使頁面刷新,因此能夠利用 hash 在不一樣源間傳遞數據。

假設 github.io 域名下 a.html 和 shaonian.eu 域名下 b.html 存在跨域請求,那麼利用 location.hash 的一個解決方案以下:

a.html 頁面中建立一個隱藏的 iframe, src 指向 b.html,其中 src 中能夠經過 hash 傳入參數給 b.html 。

b.html 頁面在處理完傳入的 hash 後經過修改 a.html 的 hash 值達到將數據傳送給 a.html 的目的。

a.html 頁面添加一個定時器,每隔必定時間判斷自身的 location.hash 是否變化,以此響應處理 。

以上步驟中須要注意第二點:如何在 iframe 頁面中修改 父親頁面的 hash 值。因爲在 IE 和 Chrome 下,兩個不一樣域的頁面是不容許 parent.location.hash 這樣賦值的,因此對於這種狀況,咱們須要在父親頁面域名下添加另外一個頁面來實現跨域請求,具體以下:

假設 a.html 中 iframe 引入了 b.html, 數據須要在這兩個頁面之間傳遞,且 c.html 是一個與 a.html 同源的頁面 
a.html 經過 iframe 將數據經過 hash 傳給 b.html 
b.html 經過 iframe 將數據經過 hash 傳給 c.html 
c.html 經過 parent.parent.location.hash 設置 a.html 的 hash 達到傳遞數據的目的 
location.bash方法的優勢在於能夠解決域名徹底不一樣的跨域請求,而且能夠實現雙向通信;而缺點則包括如下幾點:

利用這種方法傳遞的數據量受到 url 大小的限制,傳遞數據類型有限 
因爲數據直接暴露在 url 中則存在安全問題 若瀏覽器不支持 onhashchange 事件,則須要經過輪訓來獲知 url 的變化 有些瀏覽器會在 hash 變化時產生歷史記錄,所以可能影響用戶體驗 window.name。

該屬性用於獲取/設置窗口的名稱。其特徵在於:一個窗口的生命週期內,窗口載入的全部頁面共享該值,且都具備對該屬性的讀寫權限。

這意味着若是不修改該值,那麼在不一樣頁面加載以後該值也不會變,且其支持長達 2MB 的存儲量。

利用該特性咱們能夠將跨域請求用以下步驟解決:

在 a.github.io/a.html 中建立 iframe 指向 b.github.io/b.html (頁面會將自身的 window.name 附在 iframe 上) 

給 a.github.io/a.html 添加監聽 iframe 的 onload 事件,在該事件中將 iframe 的 src 設置爲本地域的代理文件(代理文件和a.html處於同一域下,能夠相互通訊),同時能夠傳出 iframe 的 name 值 獲取數據後銷燬 iframe,釋放內存,同時也保證了安全 window.name的優點在於巧妙地繞過了瀏覽器的跨域訪問限制,但同時它又是安全操做。

window.postMessage

HTML5 爲了解決這個問題,引入了一個全新的 API:跨文檔通訊 API(Cross-document messaging)。這個 API 爲 window 對象新增了一個 window.postMessage 方法,容許跨窗口通訊,不論這兩個窗口是否同源。

API 的詳細使用方法請見 MDN 。

相關文章
相關標籤/搜索