一、什麼是跨域 當a.qq.com功能變數名稱下的頁⾯或腳本試圖去請求b.qq.com功能變數名稱下的資源時,就是典型的跨域行為。跨域的定義從受限範圍可以分為兩種,⼴義跨域和狹義跨域。 (一)廣義跨域 ⼴義跨域通常包含以下三種⾏為:1. 資源跳轉:a鏈接、重定向。2. 資源嵌⼊:<link>、<script>、<i ...
一、什麼是跨域
當a.qq.com功能變數名稱下的頁⾯或腳本試圖去請求b.qq.com功能變數名稱下的資源時,就是典型的跨域行為。跨域的定義從受限範圍可以分為兩種,⼴義跨域和狹義跨域。
(一)廣義跨域
⼴義跨域通常包含以下三種⾏為:
1. 資源跳轉:a鏈接、重定向。
2. 資源嵌⼊:<link>、<script>、<img>、<frame>等dom標簽,還有樣式中background:url()、@font-face()等⽂件外鏈。
3. 腳本請求:瀏覽器存儲數據讀取、dom和js對象的跨域操作、js發起的ajax請求等。
其中,資源跳轉和資源嵌⼊⾏為可以正常請求到跨域資源,腳本請求在未經任何處理的情況下,通常會有跨域
(二)狹義跨域
狹義跨域正是瀏覽器同源策略限制的⼀類請求場景,即我們通常說的跨域⾏為,通常包含以下三種⾏為:
1. cookie、localStorage和indexDB⽆法讀取。
2. dom和js對象⽆法獲取和操作。
3. ajax請求⽆法發送
二、常見的跨域場景
瀏覽器安全的基石就是“同源策略”,即如果A網站設置的cookie,B網站如果想訪問必須滿足三個要求:同一種協議、同一個功能變數名稱、同一個埠號。
舉例來說,http://www.example.com/dir/page.html這個網址,協議是http://,功能變數名稱是www.example.com,埠號是80(可以省略,http預設80,https預設443)
例如:
http://www.example.com/dir2/other.html :同源
http://example.com/dir/other.html :不同源(功能變數名稱不同)
http://v2.www.example.com/dir/other.html :不同源(功能變數名稱不同
http://www.example.com:81/dir/other.html :不同源(埠不同)
三、跨域解決方法
(一)ajax跨域請求解決方案
1.jsonp跨域解決
jsonp (JSON with Padding),是JSON的⼀種“使⽤模式”,可以讓⽹頁跨域讀取數據。其本質是利⽤script標簽的開放策略,瀏覽器傳遞callback參數到後端,後端返回數據時會將callback參數作為函數名來包裹數據,從⽽瀏覽器就可以跨域請求數據並定製函數來⾃動處理返回數據。
代碼示例:var script = document.createElement('script'); script.type = 'text/javascript'; // 傳參callback給後端,後端返回時執⾏這個在前端定義的回調函數 script.src = 'http://a.qq.com/index.php?callback=handleCallback'; document.head.appendChild(script); // 回調執⾏函數 function handleCallback(res) { alert(JSON.stringify(res)); }
- jsonp跨域優點:
jsonp相容性強,適⽤於所有瀏覽器,尤其是IE10及以下瀏覽器。
- jsonp跨域缺點:
沒有關於調⽤錯誤的處理。
只⽀持GET請求,不⽀持POST請求以及⼤數據量的請求,⽽且也⽆法拿到相關的返回頭,狀態碼等數據。
callback參數惡意註⼊,可能會造成xss漏洞。
2.跨域資源共用(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請求分成兩類:簡單請求和⾮簡單請求。簡單請求需要滿⾜以下兩⼤條件:
1. 請求⽅法是以下三種⽅法之⼀:HEAD、GET、POST。
2. HTTP的頭信息不超出以下⼏種欄位:Accept、Accept-Language、Content-Language、Last-Event-ID、Content-Type:只限
於三個值application/x-www-form-urlencoded、multipart/form-data、text/plain。
CORS簡單請求跨域實現流程:
// IE8/9需⽤XDomainRequest相容 var xhr = new XMLHttpRequest(); // 前端設置是否帶cookie xhr.withCredentials = true; xhr.open('post', 'http://a.qq.com/index.php', true); xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); xhr.send('user=saramliu'); xhr.onreadystatechange = function() { if (xhr.readyState == 4 && xhr.status == 200) { alert(xhr.responseText); } };
- CORS跨域優點:
⽀持所有類型的HTTP請求,功能完善。
通過onerror事件監聽進⾏調⽤錯誤處理;
通過Access-Control-Allow-Origin進⾏資源訪問授權。
- CORS跨域缺點:
⽬前主流瀏覽器(IE10及以上)都⽀持CORS,但IE8和IE9需要使⽤XDomainRequest對象進⾏相容,IE7及以下瀏覽器不⽀持
3.伺服器代理
伺服器代理,顧名思義即在發送跨域請求時,後端進⾏代理中轉請求⾄伺服器端,然後將獲取的數據返回給前端。⼀般適⽤於以下場景:
針對IE7及以下瀏覽器摒棄Flash插件的情況,配置代理接⼝與前端頁⾯同源,並中轉⽬標伺服器接⼝,則ajax請求不存在跨域問題。外⽹前端頁⾯⽆法訪問內⽹接⼝,配置代理接⼝允許前端頁⾯訪問,並中轉內⽹接⼝,則外⽹前端頁⾯可以跨域訪問內⽹接⼝。
伺服器代理實現流程:
- 伺服器代理優點:
在不使⽤Flash的情況下,相容不⽀持CORS的瀏覽器跨域請求。
- 伺服器代理缺點:
後端需要⼀定的改造⼯作量。
(⼆)前端跨域通信解決⽅案
前端跨域通信是指瀏覽器中兩個不符合同源策略的前端頁⾯進⾏通信。
1.document.domain+iframe
document.domain+iframe⽅案代碼⽰例:
<!-- A頁⾯ http://a.qq.com/a.html --> <iframe id="iframe" src="http://b.qq.com/b.html"></iframe> <script> document.domain = "qq.com"; var windowB = document.getElementById("iframe").contentWindow; alert("B頁⾯的user變數:" + windowB.user); </script>
<!-- B頁⾯ http://b.qq.com/b.html --> <script> document.domain = "qq.com"; var user = "saramliu"; </script>
- document.domain+iframe⽅案優點:
實現邏輯簡單,⽆需額外中轉頁⾯
- document.domain+iframe⽅案缺點:
僅適⽤於主域相同,⼦域不同的前端通信跨域場景
2.location.hash+iframe
當兩個不符合同源策略且主域不同的頁⾯需要進⾏跨域通信時,可以利⽤url的hash值改變但不刷新頁⾯的特性,實現簡單的前端跨域通信。
<!-- A頁⾯ http://a.qq.com/a.html --> <iframe id="iframe" src="http://b.qq1.com/b.html"></iframe> <script> // 監聽c.html傳來的hash值 window.onhashchange = function () { alert("B頁⾯傳遞數據:" + location.hash.substring(1)); }; </script> <!-- B頁⾯ http://b.qq1.com/b.html --> <iframe id="iframe" src="http://a.qq.com/c.html"></iframe> <script> // 向c.html傳遞hash值 var iframe = document.getElementById('iframe'); setTimeout(function() { iframe.src = iframe.src + '#user=saramliu'; }, 1000); </script> <!-- C頁⾯ http://a.qq.com/c.html --> <script> // 監聽b.html傳來的hash值 window.onhashchange = function () { // 操作同域a.html的hash值,傳遞數據 window.parent.parent.location.hash = window.location.hash.substring(1); }; </script>
- location.hash+iframe⽅案優點
可以解決主域不同的前端通信跨域問題。
hash改變,頁⾯不會刷新。
- location.hash+iframe⽅案缺點:
受部分瀏覽器安全機制限制,需要額外的同源中轉頁⾯,且中轉頁⾯需要js邏輯來修改hash值。
通信數據類型及長度均受限,且數據外顯在url上,存在⼀定安全風險。
3.window.name+iframe
window.name屬性的獨特之處在於,name值在不同頁⾯(甚⾄不同功能變數名稱)載入後依舊存在,並且可以⽀持⾮常長的name值(2MB)。
window.name+iframe代碼⽰例:
<!-- A頁⾯ http://a.qq.com/a.html --> <iframe id="iframe" src="http://b.qq1.com/b.html"></iframe> <script> var state = 0; var iframe = document.getElementById('iframe'); iframe.onload = function() { if (state === 1) { // 第2次onload成功後,讀取同域window.name中數據 alert(iframe.contentWindow.name); } else if (state === 0) { // 第1次onload成功後 state = 1; } }; </script>
<!-- B頁⾯ http://b.qq1.com/b.html --> <script> window.name = "這⾥是B頁⾯!"; window.location = "http://a.qq.com/c.html"; </script>
- window.name+iframe⽅案優點:
可以解決主域不同的前端通信跨域問題。
通信數據類型不受限,且長度可達2MB。
- window.name+iframe⽅案缺點:
需要額外的同源中轉頁⾯,但中轉頁可以為空⽩頁。
4.postMessage
postMessage是HTML5 XMLHttpRequest Level2中的API,且是為數不多可以跨域操作的window屬性之⼀,它通常⽤於解決以下⽅⾯的問題:
1. 頁⾯和其打開的新窗⼝的數據傳遞。
2. 多窗⼝之間消息傳遞。
3. 頁⾯與嵌套iframe消息傳遞。
postMessage是⼀種安全的跨域通信⽅法。當a.html獲得對b.html的window對象後,a.html調⽤postMessage⽅法分發⼀個MessageEvent消息。b.html通過監聽message事件即可獲取a.html傳遞的數據,從⽽實現跨域通信。
- postMessage⽅法的語法如下:
otherWindow.postMessage(message、targetOrigin、[transfer])
otherWindow:⽬標窗⼝的window對象,⽐如iframe的contentWindow屬性、執⾏window.open返回的window對象等。
message:將要發送給其他window的數據。
targetOrigin:指定哪些窗⼝能接收到消息事件,其值可以是字元串*(表⽰⽆限制)或者是“協議+主機+端⼝號”。
transfer(可選):是⼀串和message同時傳遞的Transferable對象,這些對象的所有權將被轉移給消息的接收⽅,⽽發送⼀⽅將不再保有所有權。
postMessage⽅案代碼⽰例:
<!-- A頁⾯ http://a.qq.com/a.html --> <iframe id="iframe" src="http://b.qq1.com/b.html"></iframe> <script> var iframe = document.getElementById('iframe'); iframe.onload = function() { var data = {meesage: "這⾥是A頁⾯發的消息"}; var url = "http://b.qq1.com/b.html"; // 向B頁⾯發送消息 iframe.contentWindow.postMessage(JSON.stringify(data), url); }; window.addEventListener("message", function(e) { alert("B頁⾯發來消息:" + JSON.parse(e.data)); }); </script>
<!-- B頁⾯ http://b.qq1.com/b.html --> <script> window.addEventListener("message", function(e) { alert("A頁⾯發來消息:" + JSON.parse(e.data)); var data = {meesage: "這⾥是B頁⾯發的消息"}; var url = "http://a.qq.com/a.html"; window.parent.postMessage(JSON.stringify(data), url); }, false); </script>
- postMessage⽅案優點:
可以解決多種類型的前端跨域通信問題;
- postMessage⽅案缺點:
相容性⽅⾯相對差⼀點,IE8及以下瀏覽器不⽀持該⽅法,IE9只⽀持postMessage傳遞string類型的數據,⽽標準的postMessage消
息數據可以是任何類型。