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