接觸前端有幾個月了,今天說一說我對前端跨域的認識和解決方案,其實在之前我對跨域並沒有什么概念
只是聽聞過這個惡名遠揚的單詞,直到有一天我遇到了他
什么是跨域
同源策略限制了從同一個源加載的文檔或腳本如何與來自另一個源的資源進行交互。這是一個用於隔離潛在惡意文件的重要安全機制。如果兩個頁面的協議,端口(如果有指定)和主機都相同,則兩個頁面具有相同的源。我們也可以把它稱為“協議/主機/端口 tuple”,或簡單地叫做“tuple". ("tuple" ,“元”,是指一些事物組合在一起形成一個整體,比如(1,2)叫二元,(1,2,3)叫三元)。(mdn)
上邊這一段話來自於MDN,反正在我看來這么一大段干(gou)凈(pi)利(bu)落(tong)的話實在是很官方,
說白了跨域就是說 主機名(域名)、協議、端口號只要有其一不同,就為不同的域,那么這個時候你去請求的話就會發生跨域
總之就是瀏覽器的一種叫做同源策略限制的東西在搞事情
同源策略限制
大概講的是請求的時候會發送一個Origin報頭到被請求的地址
然后被請求的地址返給請求者一個Access-Control-Allow-Origin響應頭,如果值為*或者和Origin的站點對應,那么就可以請求,如果不對應就會跨域
-
同源策略限制這個東西我覺得主要就是針對請求,如果沒有的話,假如你進入了一個需要登陸的網站,你登陸完成之后,瀏覽器保存了你的cookie,然后你在不退出的情況下中途訪問了其他的惡意網站,這個惡意網站就可以盜取你的登陸信息去直接登陸你的賬號
-
還有一種就是這個同源策略限制了dom的查詢比如說我們項目中用到過iframe 這個標簽 ,他可以將一個html頁面嵌入到當前的頁面中,如果說一個網站把另一個你信任的網站通過iframe嵌入到自己的網站的話,那么你輸入的一切東西都會被人家看的一清二楚,有了同源策略的話一般都需要去代理其他域名或者加入白名單,才能引入那個域名的html,
可以說是同源策略限制給了用戶一個基本的保護
url的組成(統一資源定位符)
一直以來,自從我接觸前端以來,我就在不停的和url打交道,but我之前都是管這個東西叫域名的,
很是尷尬,
url中文叫做統一資源定位符,是互聯網上一處資源的地址,一個完整的url主要有一下及部分組成
- 協議部分 ,比較常見的有http https ftp 等
- 域名部分,比如說www.baidu.com 這一塊,也可以用服務器的ip作為域名使用
- 端口 ,可以不在瀏覽器中輸入,不輸入的時候瀏覽器默認的端口是80
- 目錄部分 ,從域名后邊以/開始以/結束,就好比我們電腦里邊文件路徑
- 文件名部分,從域名到路徑,然后同理后邊的是文件命名部分,比如說xxx.html,也可以不寫這個文件命名,不寫的時候會使用你的服務器設置的默認的文件命名
- 錨部分 從#開始,#代表頁面中的一個位置,右邊的字符就是這個位置的標識符,如果頁面中有相對應的錨點的或者對應的id的話,瀏覽器會自動根據錨后邊的字符串把可視視圖定位到頁面的錨點位置
(這個錨部分,用處非常多,到時候單獨整理一盤博客) - 參數部分 從? 開始,想必大家之前都見過有些網站的連接后邊跟着一些參數,這些參數可以傳給服務端,而且這一部分允許有多個參數,可以用&作為分隔符
跨域的幾種常用解決方法
-
script標簽,瀏覽器禁止了跨域的請求訪問,但是可以引入其他域的script腳本,並能使用其中的方法,常用的瀏覽器可以通過load事件判斷script是否加載完畢,ie是通過readystatechange屬性
-
postMessage ,這是html5新加的一個新特性,跨文檔消息傳輸Cross Document Messaging。
-
window.postMessage()方法可以安全的實現跨域通信,當方法被調用的時候,會在所有頁面的腳本執行完畢之后,向目標窗口發送一個messageEvent的一個消息
-
otherWindow.postMessage(message, targetOrigin, [transfer]);
-
otherWindow
其他窗口的一個引用,比如iframe的contentWindow屬性、執行window.open返回的窗口對象、或者是命名過或數值索引的window.frames。 -
message
將要發送到其他 window的數據。 -
targetOrigin
通過窗口的origin屬性來指定哪些窗口能接收到消息事件,其值可以是字符串"星號"(表示無限制)或者一個URI。在發送消息的時候,如果目標窗口的協議、主機地址或端口這三者的任意一項不匹配targetOrigin提供的值,那么消息就不會被發送;只有三者完全匹配,消息才會被發送。 -
transfer 可選
是一串和message 同時傳遞的 Transferable 對象. 這些對象的所有權將被轉移給消息的接收方,而發送一方將不再保有所有權。
-
在接收的window窗口下邊可以增加一個監聽
window.addEventListener("message", receiveMessage, false);
function receiveMessage(event)
{
//回調
}
message對象有四個屬性
-
data是第一個傳的參數
-
origin 表示調用 postMessage 時消息發送方窗口的 origin . 包含了協議,域名和端口
-
source屬性 發送消息的窗口對象的引用
-
JsonP跨域
上邊說了script方法 其實這個可以說成是一種,另外補充下比如img link等 都具有跨域訪問的能力
jsonp其實使用這個特性來實現跨域的,在jsonp里邊可以定義回調函數,jsonp並不能替代請求,記得有一次微信公眾號獲取token跨域,jsonp並不能達到相應的效果,感覺jsonp 就是把script標簽給封裝了一下
- CORS訪問驗證
W3C推薦了一種跨域的訪問驗證的機制,即CORS(Cross-Origin Resource Sharing 跨源資源共享)。 這種機制讓Web應用服務器能支持跨站訪問控制,使跨站數據傳輸更加安全,減輕跨域HTTP請求的風險。
CORS驗證機制需要客戶端和服務端協同處理
現在的各主流的瀏覽器都會對動態的跨域請求進行特殊的驗證處理。驗證處理分為簡單請求驗證處理和預先請求驗證處理。
- 簡單請求
當請求的方法是 GET HEAD POST 且請求頭的Content-Type是- application/x-www-form-urlencoded
- multipart/form-data
- text/plain
之一的時候,瀏覽器會直接發送跨域請求,並在請求頭中攜帶訪問地址的header,
當服務端接收到請求之后,會根據自己的跨域規則,通過Access-Control-Allow-Origin和Access-Control-Allow-Methods響應頭,來返回驗證結果。
如果驗證失敗則不會返回資源
- 預先請求
與簡單請求對應的 ,上述的六個條件,只要有一條不符合,瀏覽器在接收到請求的時候並不會立即執行請求的代碼,而是會先發送一個option請求,用於訪問服務器是否符合規范
正常來講我們發送請求,比如post請求的時候,瀏覽器也會默認給我們加上一個options請求,他的主要用途就是獲取服務器支持的http請求房和,和判斷安全,
如果驗證成功則會發送需要執行的請求,如果驗證失敗的時候則會返回一個403的狀態
以上方法基本上就可以應對大部分的跨域的需求了,還有一些其他的方法比如說document.domain+iframe
如果是都在一個主源里邊的話可以用一下,對於不同的源是無能為力的,沾個例子
//www.a.com上的a.html
document.domain = 'a.com';
var ifr = document.createElement('iframe');
ifr.src = 'http://script.a.com/b.html';
ifr.style.display = 'none';
document.body.appendChild(ifr);
ifr.onload = function(){
var doc = ifr.contentDocument || ifr.contentWindow.document;
// 在這里操縱b.html
alert(doc.getElementsByTagName("h1")[0].childNodes[0].nodeValue);
};
//script.a.com上的b.html
document.domain = 'a.com';
比如上邊這個www.a.com和script.a.com 他們的js是可以互通的,然后說明一下就是我們在日常用經常看到的www開頭的域名 其實是二級域名
這里例子里邊a.com才是主域名
以上是我對跨域和常用的解決方法的一些認知,有不足之處請多批評,我要去寫bug了