前端跨域

HTML跨域

傳統跨域方式

JSONP跨域

說到傳統跨域方式,JSONP是最普遍爲人所知的形式了。html

對於JS來講,利用XMLHttpRequest沒法請求非本域上的數據,可是卻能夠加載非本域的JS文件。
JSONP就是利用了這個所謂的「漏洞」。前端

試想當咱們在文檔中插入一個script標籤,請求一個 JS 文件時,該JS文件的內容是直接調用一個函數,
同時傳入適當的數據。則對於方法內部,就已經能夠經過參數的形式獲取傳入的數據。以下所示:ajax

640?wx_fmt=png&wxfrom=5&wx_lazy=1

此處的對象,就是傳入的數據,而fun則是那個函數。該函數其實能夠是任意函數名,只需在咱們的window域下定義,也能夠根據需求定義在其餘地方。問題在於傳入的數據怎麼來呢?其實這些對於前端來講徹底不用管,只需讓後端處理好後拼接成如上形式返回給咱們便可。而fun裏面放什麼,則取決於你要對返回的數據作什麼樣的處理了。json

另外,fun的函數名建議是隨同跨域接口一塊兒傳輸給server,以讓server來進行拼接出合適的執行代碼。完整代碼以下:後端

640?wx_fmt=png&wxfrom=5&wx_lazy=1

document.domain

也許你會遇到這樣的情形:個人網頁內嵌了一個iframe,該iframe所引用的頁面跟我本域不同,此時我想修改iframe中的內容,該怎麼辦呢?跨域

或許你認爲能夠經過contentWindow獲取iframe中的內容,但很惋惜的是咱們拿到這個contentWindow,卻不能獲取到裏面的屬性與方法(H5最新屬性postMessage除外,後面會說起)。數組

640?wx_fmt=png&wxfrom=5&wx_lazy=1

這是由於瀏覽器的同源策略禁止了咱們這樣的操做,由於它們分屬不一樣的源。瀏覽器

此時就須要用到document.domain了,也就是咱們只需將iframe中的document.domain設置成跟父頁面相同的值便可。安全

好比當 http://loliner.baidu.com/inde... 內嵌了 http://baidu.com/example.html ,此時咱們只需將 index.html 的 document.domain 修改爲 baidu.com便可實現與example.html通訊。服務器

可是問題來了,瀏覽器對document.domain的設置是有限制的,其只能設置成比自身域更高一級的域名,包括自身域。

好比存在http://b.a.com/index.html,則該頁面 document.domain容許的值爲

b.a.com

a.com

而c.b.a.com則是不容許的。且兩個頁面若要通信,則二者的主域必須相同。

上例中,主域就爲 baidu.com。

window.name

在一個窗口(window)的生命週期內,窗口載入的全部的頁面都是共享一個window.name,每一個頁面對window.name都有讀寫的權限。

以上是對window.name的定義。試想若是有兩個不一樣域的頁面順次被同一個窗口加載,那麼對於這兩個頁面來講,二者就能共享window.name,從而實現通訊。

如何順次加載?利用window.location改變窗口的地址就能夠辦到這一點。

好比,http://example.com/a.html 徹底加載後,修改window.name爲指定值。而後經過window.location將窗口定向到 http://loliner.com/b.html,此時b.html中window.name的值就爲上一個頁面中設置的值。

window.name的值只能是一個字符串,最大2M,但對咱們來講已經夠用了。若是你要傳遞對象或數組,進行JSON格式化便可。

可是,雖然這樣實現了數據通訊,可是咱們不能來回這樣切來切去,那怎麼辦呢?這時候就能夠利用 iframe + contentWindow 了。

咱們能夠在 b.html 頁面添加一個隱藏的iframe,該iframe指向a.html,當a.html徹底加載並修改了window.name以後,咱們將iframe從新指向與b.html在同一個域名下的c.html,此時就能夠經過 iframe 的 contentWindow 獲取到 name 的值了。

完整代碼以下:

640?wx_fmt=png&wxfrom=5&wx_lazy=1

H5跨域方式

window.postMessage

咱們知道在同一個頁面嵌入多個iframe,每一個iframe的域都不同時,雖然咱們能夠獲取iframe的window對象,卻不能訪問其中的屬性和方法。但H5的最新屬性postMessage除外,那麼咱們是否能夠在此利用這樣的一個「漏洞」來進行跨域呢?

答案是能夠的。

咱們只需在iframe中設置onmessage監聽,而後在父級頁面中獲取iframe的window對象,將父頁面所要傳遞的信息經過window.postMessage發出便可。

完整代碼以下:

首先要讓iframe監聽onmessage事件。

640?wx_fmt=png&wxfrom=5&wx_lazy=1

接下來則是在父頁面中獲取iframe的window對象並調用postMessage傳遞信息。

640?wx_fmt=png&wxfrom=5&wx_lazy=1

注意,此處的postMessage所發送的消息在低版本瀏覽器下只能爲字符串,若要傳遞對象或數組,只能JSON格式化。

另外,第二個參數指的是域,表示你要將信息發送到哪一個域,參數格式爲協議+主機+端口,其實跟上面所說document.domain的值是一回事。

CORS

在全部以上提到的跨域方法中,不得不說都是利用了大大小小的漏洞來繞過同源策略。而不管如何進行繞過,有一點能夠確定的是,WEB開發當中確實須要跨域獲取資源。

因此,W3C爲咱們制訂了 CORS 跨域訪問機制。CORS對於開發者來講最大的好處就是,無需考慮以什麼樣的方式繞過同源策略請求跨域資源,直接使用ajax便可。

那麼誰去負責跨域的安全性呢?答案是由Server進行控制。

Server在接收到請求的時候,需判斷請求來源,若是該來源合法,返回正常數據,同時須要在請求返回頭response header中添加必要 CORS 字段。

對於瀏覽器來講,若該請求的返回頭信息中,包含該 CORS 字段,那麼瀏覽器就會正常讀取返回的信息,不然,就會拋出No 'Access-Control-Allow-Origin' header is present on the requested resource.禁止跨域異常。

上面所說到的CORS字段,分爲必須與可選:

Access-Control-Allow-Origin(必須)

該服務器所容許跨域的源,若是爲 * ,則表示容許全部源對該服務器進行請求。
發送請求的一方的域,必須包含在該值所指定的源內。

Access-Control-Allow-Credentials(可選)

表示是否容許發送cookie,該值爲一個布爾值,默認爲false。

Access-Control-Expose-Headers(可選)

獲取response header中其餘字段的值。

因此,簡單的說,CORS 模式中的跨域請求其實與前端開發者已經沒有太大的關係,只要Server端控制好接口數據,並按需寫入CORS字段便可。

另外,CORS跨域請求能夠分爲簡單請求與複雜請求。

簡單請求

對於簡單請求,瀏覽器直接發送CORS請求便可,跟上面所說流程一致。

HEAD、GET、POST都屬於簡單請求。

複雜請求

對於複雜請求,則須要首先發出預檢請求,判斷是否能夠跨域,而後再發送真實請求。

PUT和DELETE,或Content-Type字段類型爲application/json的都屬於複雜請求。

就目前來講,JSONP 及 CORS 算是最經常使用的跨域形式了,也能知足咱們的絕大部分需求。

相對於JSONP,CORS W3C規定的標準跨域方式,功能更強大,安全性也更好。

JSONP只支持GET請求,而 CORS 支持多種請求。

JSONP 沒有域的概念,CORS 則有精確的指定哪些域能夠獲取資源。

CORS 將域的安全性管理徹底交給Server管理,更加安全。

Flash跨域

Flash跨域也有多種形式,此處只介紹最經常使用的 crossdomain.xml 策略文件 模式及allowDomain 權限授予。

corssdomain.xml 跨域策略文件

與 CORS 複雜請求同樣,Flash在進行跨域請求時,默認首先會發送預檢請求,判斷本域是否合法。該預檢請求會去跨域請求的根目錄獲取 corssdomain.xml 文件,該文件包含了跨域策略信息。好比最重要的一點,就是告訴Flash哪些域能夠跨域請求該服務器。

舉個栗子,http://baidu.com/a.html下有某swf文件要向http://loliner.com/請求資源。此時會通過以下步驟:

  1. Flash發現請求須要跨域,則首先請求http://loliner.com/crossdomai...文件;

  2. 若crossdomain.xml文件能正常返回,則分析其中的策略,查看本域是否合法;

  3. 若本域合法,則發送跨域請求;若不合法,則直接封殺請求。

此處注意,若是本域非法,Flash就會直接封殺了請求,那麼咱們在瀏覽器控制檯是捕獲不到該請求的,由於就沒發出去。

那麼Flash是怎麼判斷本域是否合法的呢?咱們先來看一下一個最簡單的corssdomain.xml文件結構。

640?wx_fmt=png&wxfrom=5&wx_lazy=1

全部的 crossdomain.xml 都以 <corss-domian-policy> 做爲根節點,裏面的每個<allow-access-from>都爲一個策略。

其中,鍵值 domain 表示容許跨域的源,此處就跟 CORS 的 Access-Control-Allow-Origin 字段同樣了。

而 secure ,則表示請求是否以加密進行傳輸。若是 corssdomain.xml 文件是從 https 協議下加載的,那麼 secure 默認爲 true。此時Flash若發現本域爲非 https 協議,縱使 domain 合法,也會封殺請求。若是secure爲false,則代表Flash能夠發送非https加密過的請求。

Security.allowDomain 跨域權限授予

若是說介紹 crossdomain.xml 文件時咱們是以 Client 端爲角度的,那麼介紹 Security.allowDomain 則必須以 Server 做爲角度。

你可能會遇到以下情形,開發了多個swf,它們之間相互依賴,但以不一樣的域發佈到網上。此時你只但願某些特定的域纔有權限去修改或調用該swf中的屬性及方法。那麼此時,你就須要用到allowDomain方法對你所容許的域進行受權了。

如下是 allowDomain 的描述:

容許指定的域中的 SWF 文件訪問調用了此方法的 SWF 文件中的對象和變量。

再舉個栗子,http://a.com/a.swf 須要調用及修改 http://b.com/b.swf 中的屬性及方法。
那麼,就必須在b.swf中調用 Security.allowDomain('a.com')以容許a.com域對本身進行訪問。

另外,要注意很是重要的一點,Security.allowDomain跨域權限授予是不對稱的。

也就是說,在上面的栗子中,只容許a.swf訪問b.swf,但b.swf卻不可以訪問a.swf。
若是非要這樣作,那麼就在a.swf中對b.com進行權限授予。

其次,對於HTTPS來講,即便調用了該方法,若試圖利用在HTTP下的swf去修改HTTPS下的swf,此時也是非法的。由於這自己就不安全。

若是非要這樣作,可讓HTTPS下的swf調用Security.allowInsecureDomain()對HTTP域進行權限授予

跨域方式對比

反觀 Flash 跨域與H5 CORS 跨域,雖然二者在形式上有所不一樣,但本質上都向着如下兩個特色靠攏:

先詢問,後請求;

安全性靠Server端來維護。

對於 CORS 簡單請求來講,瀏覽器是不會進行預檢請求發送的,這樣一個請求搞定全部的事,卻不必定徹底合理。由於對於瀏覽器來講,我在沒有知道Server是否定爲個人請求合法前,就已經拋出了大量的查詢數據。這就有可能形成數據傳輸的冗餘。

另外,若是因爲開發過程不當,頻繁發送不合法的跨域請求,自己就是一個資源的浪費。

因此,才須要到複雜請求。

而對於 Flash crossdomain.xml 這種方式(與 CORS 複雜請求相似),就避免了頻繁發送不合法的跨域請求,由於在crossdomain.xml返回以前,Flash是不會發送任何跨域請求,直接在本地就將其封殺。同時crossdomain.xml並不是每次跨域前都會請求,其有一個有效期。

但若我自己只需請求一次數據,但Server因爲業務繁重卻給我返回一個龐大的crossdomain.xml,這反而又顯得不合理了。

文章來源:http://mp.weixin.qq.com/s/_KA...

相關文章
相關標籤/搜索