導語 | 在日常開發過程中,我們通常都會遇到ajax跨域請求或者前端跨域通訊的開發場景。無論是前端同學還是後端同學都需要具備解決跨域問題的能力。本文總結梳理了常見的跨域場景、跨域解決方案及其優缺點,希望可以作為大家解決跨域問題的參考。
一、什麼是跨域
當a。qq。com域名下的頁面或指令碼試圖去請求b。qq。com域名下的資源時,就是典型的跨域行為。跨域的定義從受限範圍可以分為兩種,廣義跨域和狹義跨域。
(一)廣義跨域
廣義跨域通常包含以下三種行為:
資源跳轉:a連結、重定向。
資源嵌入:、、、等dom標籤,還有樣式中background:url()、@font-face()等檔案外鏈。
指令碼請求:瀏覽器儲存資料讀取、dom和js物件的跨域操作、js發起的ajax請求等。
其中,資源跳轉和資源嵌入行為可以正常請求到跨域資源,指令碼請求在未經任何處理的情況下,通常會有跨域問題。
(二)同源策略
同源策略(Same origin policy, SOP)是一種約定,由Netscape公司1995年引入瀏覽器,它是瀏覽器最核心也最基本的安全功能,如果缺少了同源策略,瀏覽器很容易受到XSS、CSFR等攻擊。所謂同源策略是指“協議+域名+埠”三者相同。
(三)狹義跨域
狹義跨域正是瀏覽器同源策略限制的一類請求場景,即我們通常說的跨域行為,通常包含以下三種行為:
cookie、localStorage和indexDB無法讀取。
dom和js物件無法獲取和操作。
ajax請求無法傳送。
二、常見跨域場景
三、跨域解決方案
(一)ajax跨域請求解決方案
日常開發過程中,絕大多數前端頁面都會向後端傳送ajax請求進行資料互動。那麼,ajax請求遇到跨域問題,如何進行解決呢。本文總結以下
四種
常見解決方案:
jsonp跨域
jsonp (JSON with Padding),是JSON的一種“使用模式”,可以讓網頁跨域讀取資料。其本質是利用script標籤的開放策略,瀏覽器傳遞callback引數到後端,後端返回資料時會將callback引數作為函式名來包裹資料,從而瀏覽器就可以跨域請求資料並定製函式來自動處理返回資料。
jsonp跨解實現流程:
jsonp跨域程式碼示例:
jsonp跨域
優點
:
jsonp相容性強,適用於所有瀏覽器,尤其是IE10及以下瀏覽器。
jsonp跨域
缺點
:
沒有關於呼叫錯誤的處理。
只支援GET請求,不支援POST請求以及大資料量的請求,而且也無法拿到相關的返回頭,狀態碼等資料。
callback引數惡意注入,可能會造成xss漏洞。
無法設定資源訪問授權。
跨域資源共享(CORS)
跨域資源共享(Cross-origin resource sharing,CORS)是一個 W3C標準,允許瀏覽器向跨域伺服器傳送請求,從而克服了ajax只能同源使用的限制。CORS需要瀏覽器和伺服器同時支援。目前,所有主流瀏覽器(IE10及以上)使用XMLHttpRequest物件都可支援該功能,IE8和IE9需要使用XDomainRequest物件進行相容。
CORS整個通訊過程都是瀏覽器自動完成,瀏覽器一旦發現ajax請求跨源,就會自動在頭資訊中增加Origin欄位,用來說明本次請求來自哪個源(協議+域名+埠)。因此,實現CORS通訊的關鍵是伺服器,需要伺服器配置Access-Control-Allow-Origin頭資訊。當CORS請求需要攜帶cookie時,需要服務端配置Access-Control-Allow-Credentials頭資訊,前端也需要設定withCredentials。
瀏覽器將CORS請求分成兩類:簡單請求和非簡單請求。簡單請求需要滿足以下
兩大條件
:
請求方法是以下三種方法之一:HEAD、GET、POST。
HTTP的頭資訊不超出以下幾種欄位:Accept、Accept-Language、Content-Language、Last-Event-ID、Content-Type:只限於三個值application/x-www-form-urlencoded、multipart/form-data、text/plain。
CORS簡單請求跨域實現流程:
CORS簡單請求跨域程式碼示例:
CORS跨域
優點
:
支援所有型別的HTTP請求,功能完善。
透過onerror事件監聽進行呼叫錯誤處理;
透過Access-Control-Allow-Origin進行資源訪問授權。
CORS跨域
缺點
:
目前主流瀏覽器(IE10及以上)都支援CORS,但IE8和IE9需要使用XDomainRequest物件進行相容,IE7及以下瀏覽器不支援。
Flash跨域(僅供IE7及以下瀏覽器參考使用)
由於IE7及以下瀏覽器預設是不相容跨域請求的,那麼在不改造後端的情況下,可以考慮使用Flash進行跨域請求。
Flash在進行跨域請求時,預設首先會發送預檢請求,檢查伺服器域名根目錄下的crossdomain。xml檔案,判斷請求域是否合法。如果域名不合法,Flash直接封殺請求;如果域名合法,則傳送真實請求,獲取資料並返回給前端頁面。
corssdomain。xml是目標域下的主策略檔案,其檔案配置規則如下:
(1)cross-domain-policy
crossdomain。xml的根元素,包含以下子元素:
site-control
allow-access-from
allow-access-from-identity
allow-http-request-headers-from
(2)site-control
是否允許載入其他策略檔案,屬性值permitted-cross-domain-policies允許的值有:
none,不允許載入策略檔案(包括該主策略檔案)。
master-only,僅允許載入主策略檔案。
by-content-type,僅允許載入 HTTP/HTTPS 協議下Content-Type為text/x-cross-domain-policy的策略檔案。
by-ftp-filename,僅允許載入FTP協議下檔名為crossdomain。xml的策略檔案。
all,允許載入任何策略檔案。
(3)allow-access-from
用於授權資料訪問的請求域,具有以下屬性:
domain,指定授予許可權的域,可以是域名或IP地址。
to-ports,指定授予許可權的socket連線埠範圍,以逗號分隔的埠列表,埠範圍透過在兩個埠號之間插入短劃線-指定。
secure,指定資訊是否經加密傳輸。
(4)allow-access-from-identity
允許有特定證書的請求域跨域訪問目標域上的資源。
(5)allow-http-request-headers-from
用於授權傳送使用者定義HTTP頭的請求域,具有以下屬性:
domain,指定授予許可權的域,可以是域名或IP地址。
headers,表明允許傳送的HTTP頭,以逗號分隔。
secure:指明資訊是否經加密傳輸。
crossdomain。xml檔案配置示例:
Flash跨域實現流程:
Flash跨域程式碼示例:
Flash跨域
優點
:
在不改動後端的情況下,可以相容IE7及以下瀏覽器的ajax跨域問題。
crossdoamin。xml檔案可以進行授權訪問控制。
Flash跨域
缺點
:
受限於瀏覽器對於Flash外掛的支援程度。
沒有呼叫錯誤的處理。
伺服器代理
伺服器代理,顧名思義即在傳送跨域請求時,後端進行代理中轉請求至伺服器端,然後將獲取的資料返回給前端。一般適用於以下場景:
針對IE7及以下瀏覽器摒棄Flash外掛的情況,配置代理介面與前端頁面同源,並中轉目標伺服器介面,則ajax請求不存在跨域問題。
外網前端頁面無法訪問內網介面,配置代理介面允許前端頁面訪問,並中轉內網介面,則外網前端頁面可以跨域訪問內網介面。
伺服器代理實現流程:
伺服器代理
優點
:
在不使用Flash的情況下,相容不支援CORS的瀏覽器跨域請求。
伺服器代理
缺點
:
後端需要一定的改造工作量。
(二)前端跨域通訊解決方案
前端跨域通訊是指瀏覽器中兩個不符合同源策略的前端頁面進行通訊。那麼,這種跨域問題,如何進行解決呢。本文總結以下四種常見解決方案:
document。domain+iframe
此方案僅適用於主域相同,子域不同的前端通訊跨域場景。如下圖所示,兩個不符合同源策略的頁面http://a。qq。com/a。html和http://b。qq。com/b。html,其主域相同為qq。com。a。html巢狀b。html,再都透過js設定document。domain為主域qq。com,則兩個頁面滿足了同源策略,從而實現了跨域通訊。
document。domain+iframe方案程式碼示例:
document。domain+iframe方案
優點
:
實現邏輯簡單,無需額外中轉頁面
document。domain+iframe方案
缺點
:
僅適用於主域相同,子域不同的前端通訊跨域場景
location。hash+iframe
當兩個不符合同源策略且主域不同的頁面需要進行跨域通訊時,可以利用url的hash值改變但不重新整理頁面的特性,實現簡單的前端跨域通訊。
通常情況下http://a。qq。com/a。html內嵌不同域的http://b。qq1。com/b。html時,受瀏覽器安全機制限制,a。html 可以修改b。html的hash值,但b。html不被允許修改不同域的父窗體a。html的hash值。因此,此方案需要一個與a。html同源的http://a。qq。com/c。html來進行中轉,此方案實現流程如下圖所示:
location。hash+iframe方案程式碼示例:
location。hash+iframe方案
優點
:
可以解決主域不同的前端通訊跨域問題。
hash改變,頁面不會重新整理。
location。hash+iframe方案
缺點
:
受部分瀏覽器安全機制限制,需要額外的同源中轉頁面,且中轉頁面需要js邏輯來修改hash值。
通訊資料型別及長度均受限,且資料外顯在url上,存在一定安全風險。
window。name+iframe
window。name屬性的獨特之處在於,name值在不同頁面(甚至不同域名)載入後依舊存在,並且可以支援非常長的name值(2MB)。
如下圖所示,http://a。qq。com/a。html內嵌不同域的http://b。qq1。com/b。html。b。html有資料要傳遞時,把資料附加到window。name上,然後跳轉到一個和a。html同域的http://a。qq。com/c。html。由於a。html和c。html滿足同源策略,a。html可以獲取c。html的window。name,從而實現了跨域通訊。
window。name+iframe程式碼示例:
window。name+iframe方案
優點
:
可以解決主域不同的前端通訊跨域問題。
通訊資料型別不受限,且長度可達2MB。
window。name+iframe方案
缺點
:
需要額外的同源中轉頁面,但中轉頁可以為空白頁。
postMessage
postMessage是HTML5 XMLHttpRequest Level2中的API,且是為數不多可以跨域操作的window屬性之一,它通常用於解決以下方面的問題:
頁面和其開啟的新視窗的資料傳遞。
多視窗之間訊息傳遞。
頁面與巢狀iframe訊息傳遞。
postMessage是一種安全的跨域通訊方法。當a。html獲得對b。html的window物件後,a。html呼叫postMessage方法分發一個MessageEvent訊息。b。html透過監聽message事件即可獲取a。html傳遞的資料,從而實現跨域通訊。postMessage實現流程如下圖所示:
postMessage方法的語法如下:
otherWindow。postMessage(message、targetOrigin、[transfer])
otherWindow
目標視窗的window物件,比如iframe的contentWindow屬性、執行window。open返回的window物件等。
message
將要傳送給其他window的資料。
targetOrigin
指定哪些視窗能接收到訊息事件,其值可以是字串*(表示無限制)或者是“協議+主機+埠號”。
transfer(可選)
是一串和message同時傳遞的Transferable物件,這些物件的所有權將被轉移給訊息的接收方,而傳送一方將不再保有所有權。
postMessage方案程式碼示例:
postMessage方案
優點
:
可以解決多種型別的前端跨域通訊問題;
postMessage方案
缺點
:
相容性方面相對差一點,IE8及以下瀏覽器不支援該方法,IE9只支援postMessage傳遞string型別的資料,而標準的postMessage訊息資料可以是任何型別。
四、總結
本文介紹了瀏覽器受同源策略限制的跨域行為以及常見的跨域場景。總結了跨域問題的經驗,並從ajax請求和前端通訊兩大方向進行梳理常用的跨域解決方法及其優缺點,希望可以作為大家在日常開發解決web跨域問題的參考。如果有描述不當之處,也希望大家隨時進行溝通和指正。
參考資料:
1。瀏覽器的同源策略
2。跨源資源共享(CORS)
3。Cross-domain AJAX using Flash
4。window。postMessage
作者簡介
劉孟
騰訊前端開發工程師
騰訊前端開發工程師,畢業於上海大學。目前負責騰訊優聯專案的前端開發工作,有豐富的系統平臺及遊戲營銷活動前端開發經驗。