做者: Yeaseon
Blog:yeaseonzhang.github.io
原文連接javascript
在開發過程當中常常會涉及跨域問題,解決跨域問題的方案也有不少種,接下來就來梳理一下前端跨域的經常使用方法。css
何爲跨域,跨域是相對於同源而言。協議、域名和端口均相同,則爲同源。
瀏覽器經過同源策略限制從一個源加載的文檔或腳本與來自另外一個源的資源進行交互。這是一個用於隔離潛在惡意文件的關鍵的安全機制,摘抄自MDN。html
這種方案主要用於主域相同,子域不一樣的跨域狀況。例如: https://jdc.jd.com/
和 https://www.jd.com/
。前端
經過在www.jd.com/打開一個jdc.jd.com/,此時JDC的域名是jdc.jd.com/
,經過控制檯執行document.domain = 'jd.com'
;。強制設置主域,實現同源。java
var jdc = window.open('https://jdc.jd.com/');
// JDC 頁面加載完成後執行
var divs = jdc.document.getElementsByTagName('div');
$(divs).css('border', '1px solid red');複製代碼
一般的作法是經過iframe
加載一個跨域頁面資源。由於window.open
這個方法在瀏覽器中會被當作談廣告禁止掉。git
domain.com/index.htmlgithub
<iframe id="sub" src="http://sub.domain.com/index.html"></iframe>
<script> var username = 'yeseonzhang'; </script>複製代碼
<script> document.domain = 'domain.com'; console.log(window.parent.username); </script>複製代碼
這種跨域方法主要是經過設置/監聽url的hash部分,來實現跨域,同時須要藉助第三個頁面來進行輔助。後端
上圖就是三個頁面的包含關係,以及hash
的傳遞過程。跨域
<iframe id="iframe-b" src="http://domain-b.com/b.html"></iframe>
<script> var bPage = document.getElementById('iframe-b'); /* step 1 */ bPage.src = bPage.src + '#user=yeaseonzhang'; function cb (res) { console.log(res); } </script>複製代碼
<iframe id="iframe-c" src="http://domain-a.com/c.html"></iframe>
<script> var cPage = document.getElementById('iframe-c'); window.onhashchange = function () { /* step 2 */ cPage.src = cPage.src + location.hash; } </script>複製代碼
<script> window.onhashchange = function () { /* step 3 */ window.parent.parent.cb('success: ' + location.hash); } </script>複製代碼
因爲a頁面和c頁面是同域資源,因此c頁面能夠經過window.parent.parent
訪問a頁面資源。
這個方案相似location.hash
,須要經過第三個頁面進行輔助。window.name
屬性是用來獲取/設置窗口的名稱。須要注意的是,當前窗口的window.name
並不會由於頁面的重載和跳轉而更改,因此能夠利用這個特性將跨域的window.name
經過重定向到同域頁面進行讀取。
<script> var iframe = document.createElement('iframe'); /* step 1 加載跨域頁面 */ iframe.src = 'http://domain-b.com/b.html'; var domain = 'diff'; /* 監聽iframe加載 */ iframe.onload = function () { if ('diff' == domain) { /* step 2 重定向到同域頁面 */ iframe.contentWindow.location = 'http://www.domain-a.com/c.html'; domain = 'same'; } else if ('same' == domain) { /* 獲取同域資源的window.name信息 */ cb(iframe.contentWindow.name); /* 清空數據 */ iframe.contentWindow.name = ''; } } function cb (res) { console.log(JSON.parse(res)); } </script>複製代碼
<scirpt>
/* 寫入相關數據 */
var obj = {
username: 'yeaseonzhang'
}
window.name = JSON.stringify(obj);
</script>複製代碼
同域c頁面,能夠是一個空頁面,不須要進行任何操做。
JSONP(JSON with Padding)是JSON的一種使用方式。這種方式容許用戶傳遞一個callback
參數給服務端,而後服務端返回數據時會將這個callback參數做爲函數名來包裹住JSON數據。
衆所周知,html頁面中全部帶有src
屬性的標籤(<img>
,<script>
和iframe
)都擁有跨域能力。因此最簡單的實現方式就是動態加載JS。
客戶端
function todo(data){
console.log('The author is: '+ data.name);
}
var script = document.createElement('script');
/* callback參數,用來指定回調函數的名字。 */
script.src = 'http://www.yeaseonzhang.com/author?callback=todo';
document.body.appendChild(script);複製代碼
服務端
/* 服務器收到這個請求之後,會將數據放在回調函數的參數位置返回。 */
todo({"name": "yeaseonzhang"});複製代碼
todo()
函數會被做爲全局函數來執行,只要定義了todo()
函數,該函數就會被當即調用。
window.postMessage
是HTML5中一個安全的,基於事件的消息API。
otherWindow.postMessage(message, targetOrigin, [transfer]);複製代碼
postMessage()
,方法包含三個參數:
message
: 消息內容targetOrigin
: 接受消息窗口的源,即"協議 + 域名 + 端口"。也能夠設置爲通配符*
,向全部窗口發送transfer
: 可選參數(布爾值),是一串和message 同時傳遞的Transferable
對象. 這些對象的全部權將被轉移給消息的接收方,而發送一方將再也不保有全部權。發送者和接收者均可以經過message
事件,監聽對方的消息。message
事件的事件對象event
包含三個屬性:
event.source
: 發送消息的窗口對象的引用,能夠用此在兩個窗口創建雙向通訊。event.origin
: 發送消息的URIevent.data
: 消息內容發送者: domain-a.com/a.html
<script> var newWindow = window.open('http://domain-b.com/b.html'); /* 向b.html發送消息 */ newWindow.postMessage('Hello', 'http://domain-b.com/b.html'); /* 雙向通訊,接收b.html的回覆消息 */ var onmessage = function (event) { var data = event.data; var origin = event.origin; var source = event.source; if (origin == 'http://domain-b.com/b.html') { console.log(data); //Nice to see you! } }; window.addEventListener('message', onmessage, false); </scirpt>複製代碼
<script> var onmessage = function (event) { var data = event.data; var origin = event.origin; var source = event.source; if (origin == 'http://domain-a.com/a.html') { console.log(data); //Hello /* 回覆a.html的消息 */ source.postMessage('Nice to see you!', 'http://domain-a.com/a.html'); } }; window.addEventListener('message', onmessage, false); </script>複製代碼
WebSocket
是一種HTML5的一種新的協議,它實現了瀏覽器與服務器的全雙工通訊,同時也是跨域的一種解決方案,詳細介紹請訪問MDN。
/* websocket協議爲ws/wss, 相似http/https的區別 */
wsUrl = 'wss://127.0.0.1:8090/ws/';
/* 發送 */
ws = new WebSocket(wsUrl);
/* 鏈接成功創建時調用 */
ws.onopen = function (event) {
console.log("websocket command onopen");
var msg = {
username: 'YeaseonZhang'
}
/* 經過 send() 方法向服務端發送消息,參數必須爲字符串 */
ws.send(JSON.stringify(msg));
};
/* 服務端向客戶端發送消息時調用 */
ws.onmessage = function (event) {
/* event.data包含了服務端發送過來的消息 */
console.log("websocket command onmessage: " + event.data);
if (event.data === 'success') {
/* 經過 close() 方法斷開websocket鏈接 */
ws.close();
}
};
/* 鏈接被關閉時調用 */
ws.onclose = function (event) {
console.log("websocket command onclose: " + event.data);
};
/* 出現錯誤時調用 */
ws.onerror = function (event) {
console.log("websocket command onerror: " + event.data);
};複製代碼
WebSocket
的優點是除了能夠實現跨域,還有就是能夠保持長鏈接,而不須要經過輪詢實現實時性。
CORS
是一個W3C標準,全稱是"跨域資源共享"(Cross-origin resource sharing)。
只須要後端同窗支持就ok,前端不須要作不少額外工做(除了攜帶cookie
)。
只要服務器返回的相應中包含頭部信息Access-Control-Allow-Origin: domain-name
,domain-name
爲容許跨域的域名,也能夠設置成*
,瀏覽器就會容許本次跨域請求。
以上就是我所瞭解的跨域的解決方案,但願對你有所幫助。