跨域

1.什么是同源策略

  • 瀏覽器出于安全方面的考慮,只允許與本域下的接口交互。不同源的客戶端腳本在沒有明確授權的情況下,不能讀寫對方的資源。

  • 同源指的是:

    • 同協議
    • 同域名
    • 同端口
  • 作用:保證用戶信息的安全,防止惡意的網站竊取數據

    • 例1:A網站是一家銀行,用戶登錄以后,又去瀏覽其他網站。如果其他網站可以讀取A網站的 Cookie,會發生什么?很顯然,如果 Cookie 包含隱私(比如存款總額),這些信息就會泄漏。除此之外,Cookie 往往用來保存用戶的登錄狀態,如果用戶沒有退出登錄,其他網站就可以冒充用戶,為所欲為。(因為瀏覽器同時還規定,提交表單不受同源政策的限制)
    • 例2:惡意網站的頁面通過iframe嵌入了銀行的登錄頁面(二者不同源),如果沒有同源限制,惡意網頁上的javascript腳本就可以在用戶登錄銀行的時候獲取用戶名和密碼,從而造成相關的風險。
  • 對于當前頁面來說頁面中 JS 文件的域不重要,重要的是當前頁面所在的域與 腳本中涉及到的域(例如xht的open方法的url)是否同源

2.什么是跨域?跨域有幾種實現形式

  • 跨域:瀏覽器出于安全方面的考慮設置了同源策略來限制不同源之間的交互,但是也阻礙了不同域之間的協助。為了實現不同源之間的交互、協作,因此需要“跨域”。

  • 跨域的幾種實現形式:

    • JSONP(填充式JSON或參數式JSON)
    • CORS(跨源資源共享)
    • 降域
    • postMessage

3.JSONP的原理是什么?

  • 基本思想是,網頁通過添加一個<script>元素,向服務器請求JSON數據,這種做法不受同源政策限制;服務器收到請求后,將數據放在一個指定名字的回調函數里傳回來。傳回后回調函數立即執行(參數是后端產生的數據),從而實現相應的功能。
  • 具體流程:
    1.網頁動態地插入<script>元素,由它向跨域網址發出請求
function addScriptTag(src){
    var script = document.createElement('script');
    script.src = src; //跨域網址
    document.body.appendChild(script);  
    // 往頁面插入元素后,會向跨域網址發出請求(src指定了跨域網址,得到響應后立即執行
}

window.onload = function(){
    addScriptTag("http://example.com/ip?callback = foo');  //當頁面加載完畢,即往頁面中插入script元素
}

function foo(data){
    console.log('your public ip address is: '+ data.ip)'
}

2.上面代碼通過動態添加<script>元素,向服務器example.com發出請求。注意,該請求的查詢字符串有一個callback參數,用來指定回調函數的名字,這對于JSONP是必需的。
3.服務器收到該請求后,會將數據放在回到函數的參數位置返回

foo({
  "ip": "8.8.8.8"
});

4.由于<script>元素請求的腳本,直接作為代碼運行。這時,只要瀏覽器定義了foo函數,該函數就會立即調用。作為參數的JSON數據被視為JavaScript對象,而不是字符串,因此避免了使用JSON.parse的步驟。

  • 注意:JSONP只能發 GET 請求(因為請求是放在<script>的scr中的)。

4.CORS是什么?

  • CORS: Cross-Origin Resource Sharing, 跨源資源共享,是W3C的一個工作草案,定義了在必須訪問跨源資源時,瀏覽器與服務器應該如何溝通,它是一種 ajax跨域請求資源的方式,支持現代瀏覽器,IE支持10以上。

  • 實現過程:當使用 XMLHttpRequest發送請求時,瀏覽器發現該請求不符合同源策略,會自動給該請求加一個請求頭:Origin,并將請求發送。服務器端收到請求后,如果確定接受請求則在返回結果中加入一個響應頭:Access-Control-Allow-Origin; 瀏覽器收到響應后判斷該相應頭中是否包含 Origin 的值,如果有則瀏覽器會處理響應,我們就可以拿到響應數據,如果不包含瀏覽器直接駁回,這時我們無法拿到響應數據。

  • 整個CORS通信過程,都是瀏覽器自動完成,不需要用戶參與

  • 實現CORS通信的關鍵是服務器。只要服務器實現了CORS接口,就可以跨源通信。

  • 詳細流程:
    1.瀏覽器發現這次請求不符合同源策略,就自動在頭信息之中,添加一個Origin字段。

GET /cors HTTP/1.1
Origin: http://api.bob.com
Host: api.alice.com
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...

在上面的頭信息中,Origin字段表明了本次請求來自哪個源:協議、域名、端口。

2.該請求到達服務器后,服務器會根據這個值來判斷是否接受請求。如果Origin指定的域名在許可范圍內,服務器返回的響應,會多出幾個頭信息字段(如下所示)。如果Origin指定的源,不在許可范圍內,服務器會返回一個正常的HTTP回應。

Access-Control-Allow-Origin: http://api.bob.com
Access-Control-Allow-Credentials: true
Access-Control-Expose-Headers: FooBar
Content-Type: text/html; charset=utf-8

3.瀏覽器收到響應后判斷該相應頭中是否包含Origin的值,如果響應的頭信息沒有包含Access-Control-Allow-Origin字段,就知道出錯了,從而拋出一個錯誤,被XMLHttpRequest的onerror回調函數捕獲,這時我們無法拿到響應數據。如果有則瀏覽器會處理響應,我們就可以拿到響應數據。

5.降域

  • 降域獲取同一Cookie:Cookie 是服務器寫入瀏覽器的一小段信息,只有同源的網頁才能共享。但是,兩個網頁一級域名相同,只是二級域名不同,瀏覽器允許通過設置document.domain共享 Cookie。
// A網頁和B網頁設置相同的document.domain
document.domain = 'example.com'
// A網頁通過腳本設置Cookie
document.cookie = "test1 = hello";
// B網頁可以獲取到該cookie
var otherCookie = document.cookie;
  • 降域使不同源的iframe窗口和父窗口相互通信:如果兩個網頁不同源,就無法拿到對方的DOM。典型的例子是iframe窗口和與父窗口無法通信。如果兩個窗口一級域名相同,只是二級域名不同,那么設置document.domain屬性,就可以規避同源政策,拿到DOM。
A 網頁
URL: http://a.yanxin.com:8080/a.html
<div class="ct">
  <h1>使用降域實現跨域</h1>
  <div class="main">
    <input type="text" placeholder="http://a.yanxin.cn:8080/a.html">
  </div>
  <iframe src="http://b.yanxin.com:8080/b.html" frameborder="0" ></iframe>
</div>

<script>

document.querySelector('.main input').addEventListener('input', function(){
  console.log(this.value);
  /* window.frames 是窗口中所有命名的框架組成的數組。
  這個數組的每個元素都是一個Window對象,對應于窗口中的一個框架。
  window.frames[0]得到的就是html中的框架
  window.frames[0].document.querySelector('input') 得到框架中的input元素
  */
  window.frames[0].document.querySelector('input').value = this.value;
})

document.domain = "yanxin.com"
</script>
iframe中的B網頁
URL: http://b.yanxin.com:8080/b.html
<input id="input" type="text"  placeholder="http://b.yanxin.com:8080/b.html">
<script>

document.querySelector('#input').addEventListener('input', function(){
    // 得到父窗口中的input元素
    window.parent.document.querySelector('input').value = this.value;
})
document.domain = 'yanxin.com';
</script>

6.postMessage

  • HTML5為了解決跨域問題,引入了一個全新的API:跨文檔通信 API(Cross-document messaging)。
    這個API為window對象新增了一個window.postMessage方法,允許跨窗口通信,不論這兩個窗口是否同源。

  • 目的:向另一個地方傳遞數據,另一個地方指的是:包含在當前頁面的<iframe>元素,或者由當前頁面彈出的窗口

  • window.postMessage() 方法被調用時,會在所有頁面腳本執行完畢之后向目標窗口派發一個 MessageEvent 消息。 * otherWindow.postMessage(message, targetOrigin, [transfer]);

    • otherWindow:其他窗口的一個引用(相對于當前的窗口的其他窗口),比如iframe的contentWindow屬性、執行window.open返回的窗口對象、或者是命名過或數值索引的window.frames。
    • message將要發送到其他 window的數據
    • targetOrigin:指定哪些窗口能接收到消息事件,其值可以是字符串"*"(表示無限制)或者一個URI。在發送消息的時候,如果目標窗口的協議、主機地址或端口這三者的任意一項不匹配targetOrigin提供的值,那么消息就不會被發送;只有三者完全匹配,消息才會被發送。
    • 注意:如果你明確的知道消息應該發送到哪個窗口,那么請始終提供一個有確切值的targetOrigin,而不是*。不提供確切的目標將導致數據泄露到任何對數據感興趣的惡意站點。
  • 示例

頁面A:  http://a.yanxin.cn:8080/a.html
在頁面A中打開頁面B: http://b.yanxin.cn:8080/b.html
當點擊頁面A上的button時,向頁面B傳輸消息"hello world"
  <button id="sendmessage">send message</button>
  <script>
   var button = document.querySelector("#sendmessage");
  // 打開頁面b,并且取得對它的引用
   var targetWindow = window.open("http://b.yanxin.cn:8080/b.html");
   button.addEventListener('click',function(){
   // 注意: 這里是向targetWindow即新打開的窗口(通過上面的window.open打開的窗口)發送消息
  // 直接打開頁面b(手動打開的)是無法收到消息的
  // 如果直接調用postMessage則相當于當前窗口向自己發送消息
      targetWindow.postMessage("hello world!!", "http://b.yanxin.cn:8080/b.html");
   });
   
  </script>
為頁面B的window添加監聽器
當頁面B收到Message時,在控制臺中輸出收到的消息,并提示"hello"
<script>
    window.addEventListener("message", receiveMessage);
    function receiveMessage(event){
        console.log(event.data);
        alert("hello");
    }
</script>

參考資料:

CORS 詳解-阮一峰
瀏覽器同源政策及其規避方法-阮一峰
Window.postMessage() - Web APIs | MDN

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,333評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,491評論 3 416
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,263評論 0 374
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,946評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,708評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,186評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,255評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,409評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,939評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,774評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,976評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,518評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,209評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,641評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,872評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,650評論 3 391
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,958評論 2 373

推薦閱讀更多精彩內容

  • 跨域問題的場景和解決方案多種多樣,只要是做前端開發,總會遇到。而且面試時也是必問的問題。所以自己學習總結記錄一下。...
    花開_陳鳳娟閱讀 739評論 0 0
  • 歡迎關注微信公眾號:全棧工廠 本文主要參考跨域資源共享 CORS 詳解[http://www.ruanyifeng...
    liqingbiubiu閱讀 1,857評論 0 3
  • 一、瀏覽器的同源策略 1.什么是同源? 所謂“同源”指的是”三個相同“。相同的域名、端口和協議,這三個相同的話就視...
    徐國軍_plus閱讀 859評論 1 3
  • 什么是同源策略 瀏覽器出于安全方面的考慮,只允許與本域下的接口交互。不同源的客戶端腳本在沒有明確授權的情況下,不能...
    ezrealor閱讀 502評論 0 1
  • 因為初三沒有考上高中,只能聽父母的話來到了現在就讀的學校,來到這個學校最幸福的事就是遇到他,最初對他的了解還是...
    Bunuan閱讀 158評論 0 0