對于前端來說,跨域是基礎工作之一。
同源策略
提到跨域,不得不先說同源策略。
同源策略是一種指瀏覽器的安全機制,只允許與本域下的接口交互。不同源的客戶端腳本在沒有明確授權的情況下,不能讀寫對方的資源。
本域是指:
- 同協議:都是http或者都是https
- 同域名: 如 http://test.com/a 和 http://test.com/b
- 同端口: 如都是3000端口
打個比方,淘寶域名的網站請求騰訊域名網站的資源,會失敗,相反,亦同(馬爸爸們怎么可能允許這種事情發生??)。
失敗?在哪失敗?沒發出去?發出去了人家服務器沒理你?還是理你了但是瀏覽器不給你看?
測一把,測試中的服務器端沒有做任何域名校驗。代碼
結果是,代碼發出去了,服務器也響應并返回了,瀏覽器中 NetWorks 請求的 Status Code也是200,但是無法看到響應體。
因而,是瀏覽器認為不安全,從而屏蔽了響應體。
跨域
再說跨域,那就是允許不同域的接口進行交互,就是POST、GET的url不是當前的網站,域名不同。(比如一個馬爸爸在某些特殊情況下竟然允許另外一個馬爸爸請求自己的資源??。。。)
跨域有幾種方式:
- JSONP
- CORS
- 降域
- postMessage
JSONP
- 無論是靜態頁面、動態頁面、還是web服務、WCR, ajax直接請求普通文件都存在跨域問題;
- 頁面中調用 js 文件不受跨域的影響(凡是擁有 src 屬性的標簽都有跨域能力, e.g. img iframe );
- 那么,如果在遠程服務器上設法把數據裝進 js 格式的文件里,供客戶端調用和進一步處理, 就可以通過web端跨域訪問到這份數據;
- js 支持 JSON,在加載src文件時,會作為 js 來執行;
綜上,在請求數據時,請求帶一個 callback 參數,參數值為處理返回數據的函數的函數名,服務器動態生成JSON數據,將這個 callback 所帶的函數名包裹住 JSON 數據,然后返回, 客戶端加載則立即執行該 callback funciton, 從而對數據進行相應的處理。
這種非正式傳輸協議,就稱作 JSONP (JSON with Padding) , 可以讓網頁從別的域名獲取數據,并將 JSON 數據填充進 callback function 從而回調。
JSONP 步驟
- 定義 callback函數 cb;
- 創建script標簽,src的地址為請求的url,最后增加參數 callback=cb;
- 服務端在收到請求后,解析參數,計算并生成 JSON 數據,輸出 cb(JSON) 字符串;
- 客戶端收到數據, 作為 js 執行,調用 cb 函數,服務器返回的 JSON 為 cb 的參數。
JSONP 實例
CORS
CORS 是指跨域資源共享(Cross-Origin Resource Sharing),是?種 ajax 跨域請求資源的?式,?持現代瀏覽器(IE 10+)。
CORS步驟:
- 使?XMLHttpRequest 發送請求時,發現該請求不符合同源策略,給該請求加?個 Origin請求頭;
- 后臺對請求的域名進行判斷,在返回結果中加入相應的響應頭 Access-Control-Allow-Origin;
- 瀏覽器判斷該響應頭中是否包含 Origin 的值,如果包含則不屏蔽響應體,文件繼續執行,反之則屏蔽響應體,該報錯報錯,該跳過跳過。
CORS 實例
核心部分
res.header("Access-Control-Allow-Origin", "http://www.aaa.com:8080");
res.header("Access-Control-Allow-Origin", "*"); // accept all the domains
降域
對于主域相同但是子域不相同的一種解決方法。
降域步驟
- 比如兩個域名分別是 a.test.com/a.html 以及 b.test.com/b.html;
- 在兩個文件中分別加上 document.domain = "test.com";
- 在 a.html 中創建一個iframe,src 為 b.html , 控制其content Document,從而兩個 js 文件就可以"交互"了。
降域 實例
postMessage
通常用于:
- 頁面和其打開的新窗口的數據傳遞
- 多窗口之間數據傳遞
- 頁面與其 iframe 數據傳遞
postMessage(data,origin) 方法接受兩個參數:
- data: 要傳遞的數據,html5規范中提到該參數可以是JavaScript的任意基本類型或可復制的對象,然而并不是所有瀏覽器都做到了這點兒,部分瀏覽器只能處理字符串參數,所以我們在傳遞參數的時候需要使用JSON.stringify()方法對對象參數序列化,在低版本IE中引用json2.js可以實現類似效果。
- origin: 字符串,指明目標窗口的源,協議+主機+端口號[+URL],URL會被忽略,所以可以不寫,這個參數是為了安全考慮,postMessage()方法只會將message傳遞給指定窗口,當然如果愿意也可以建參數設置為"*",這樣可以傳遞給任意窗口,如果要指定和當前窗口同源的話設置為"/"。
window.postMessage() 方法被調用時,會在所有頁面腳本執行完畢之后向目標窗口派發一個 MessageEvent.
postMessage 實例
核心部分
// a.test.html
const input = document.querySelector('input')
input.addEventListener('input', function () {
window.frames[0].postMessage(this.value, '*')
})
// 監聽其它頁面發送來的消息
window.addEventListener('message', function (e) {
input.value = e.data;
});
// b.test.html
const input = document.querySelector('input')
input.addEventListener('input', function () {
window.parent.postMessage(this.value, '*')
})
// 監聽其它頁面發送來的消息
window.addEventListener('message', function (e) {
input.value = e.data;
});
降域和postMessage嘛,我個人感覺是沒有另外兩種使用廣泛,JSONP、CORS都是實打實的需要后端配合的,然而如果后端童鞋不鳥你,那...
綜上所述,后端同樣是前端的必備技能啊,666.....