整理前端最全跨域方案,工作面試不用愁(上)

關(guān)于前端跨域這個(gè)老生長談的問題,解決方案也是鋪天蓋地,但大家是否真正從原理上到實(shí)踐已經(jīng)完全掌握了呢?小郭今天將總結(jié)所有的跨域解決方案,讓你在面試中收獲滿分。

本文將從跨域場景到跨域方案逐一說明,重點(diǎn)突出最常用跨域方案,讓你工作開發(fā)不用愁。
由于本文涉及篇幅較長,因此分上下集講述,力爭把最全面的跨域問題分享給大家。

概念

廣義跨域:一個(gè)域下的文檔或腳本試圖去請(qǐng)求另一個(gè)域下的資源,這被稱作為廣義上跨域。
舉例:

  • 資源跳轉(zhuǎn): A鏈接、重定向、表單提交
  • 資源嵌入: <link>、<script>、<img>、<frame>等dom標(biāo)簽,還有樣式中background:url()、@font-face()等文件
  • 外鏈腳本請(qǐng)求: js發(fā)起的ajax請(qǐng)求、dom和js對(duì)象的跨域操作等

我們經(jīng)常討論的跨域是從狹義角度去理解,即:由瀏覽器同源策略限制的一類請(qǐng)求場景。先來解釋一下,什么是同源策略?

同源策略/SOP(Same origin policy)是一種約定,由Netscape公司1995年引入瀏覽器,它是瀏覽器最核心也最基本的安全功能,如果缺少了同源策略,瀏覽器很容易受到XSS、CSFR等攻擊。

所謂同源是指"協(xié)議+域名+端口"三者相同且必須相同,即便兩個(gè)不同的域名指向同一個(gè)ip地址,也非同源。

一張圖說明同源策略限制的的場景:

以上就是關(guān)于跨域的原理,那接下來整理一下前端都有哪些跨域解決方案,哪些是最常用的方案。

方案

  • 通過jsonp跨域
  • document.domain + iframe
  • 跨域location.hash + iframe
  • window.name + iframe跨域
  • postMessage跨域跨域資源共享(CORS)
  • nginx代理跨域
  • nodejs中間件代理跨域
  • WebSocket協(xié)議跨域

以上方案相信大家或多或少都有所了解,在這里重點(diǎn)突出常用方案。

方案一:通過jsonp跨域

通常為了減輕web服務(wù)器的負(fù)載,我們把js、css,img等靜態(tài)資源分離到另一臺(tái)獨(dú)立域名的服務(wù)器上,在html頁面中再通過相應(yīng)的標(biāo)簽從不同域名下加載靜態(tài)資源,而被瀏覽器允許,基于此原理,我們可以通過動(dòng)態(tài)創(chuàng)建script,再請(qǐng)求一個(gè)帶參網(wǎng)址實(shí)現(xiàn)跨域通信。但只能實(shí)現(xiàn)get一種請(qǐng)求。不推薦

方案二:document.domain + iframe跨域

兩個(gè)頁面都通過js強(qiáng)制設(shè)置document.domain為基礎(chǔ)主域,就實(shí)現(xiàn)了同域。因此此方案僅限主域相同,子域不同的跨域應(yīng)用場景。不推薦

方案三: location.hash + iframe跨域

a欲與b跨域相互通信,通過中間頁c來實(shí)現(xiàn)。 三個(gè)頁面,不同域之間利用iframe的location.hash傳值,相同域之間直接js訪問來通信。

實(shí)現(xiàn)方式:A域:a.html -> B域:b.html -> A域:c.html,a與b不同域只能通過hash值單向通信,b與c也不同域也只能單向通信,但c與a同域,所以c可通過parent.parent訪問a頁面所有對(duì)象。因?yàn)閷?shí)現(xiàn)比較繁瑣,故不推薦

方案四: window.name + iframe跨域

window.name屬性的獨(dú)特之處在于name值在不同的頁面(甚至不同域名)加載后依舊存在,并且可以支持非常長的 name 值。

請(qǐng)求頁:http://www.domain1.com/a.html

var proxy = function(url, callback) {   
   var state = 0;    
   var iframe = document.createElement('iframe');    // 加載跨域頁面    
   iframe.src = url;    // onload事件會(huì)觸發(fā)2次,第1次加載跨域頁,并留存數(shù)據(jù)于    
   window.name    iframe.onload = function() {       
    if (state === 1) {            // 第2次onload(同域proxy頁)成功后,讀取同域window.name中數(shù)據(jù)            
      callback(iframe.contentWindow.name);            
      destoryFrame();        
    } else if (state === 0) {            // 第1次onload(跨域頁)成功后,切換到同域代理頁面            
      iframe.contentWindow.location = 'http://www.domain1.com/proxy.html';                
      state = 1;       
    }   
 };    
  document.body.appendChild(iframe);    // 獲取數(shù)據(jù)以后銷毀這個(gè)iframe,釋放內(nèi)存;這也保證了安全(不被其他域frame js訪問)    
  function destoryFrame() {        
    iframe.contentWindow.document.write('');        
    iframe.contentWindow.close();        
    document.body.removeChild(iframe);    
  }
};// 請(qǐng)求跨域b頁面數(shù)據(jù)
proxy('http://www.domain2.com/b.html', function(data){    
  alert(data);
});

代理頁:http://www.domain1.com/proxy

中間代理頁,與a.html同域,無需添加內(nèi)容

被請(qǐng)求頁:http://www.domain2.com/b.html

<script>window.name = 'This is domain2 data!';</script>

通過iframe的src屬性由外域轉(zhuǎn)向本地域,跨域數(shù)據(jù)即由iframe的window.name從外域傳遞到本地域。這個(gè)就巧妙地繞過了瀏覽器的跨域訪問限制,但同時(shí)它又是安全操作。但操作依然復(fù)雜,不推薦使用

方案五:postMessage跨域

postMessage是HTML5 XMLHttpRequest Level 2中的API,且是為數(shù)不多可以跨域操作的window屬性之一,他可以解決這類問題:頁面和其打開的新窗口的數(shù)據(jù)傳遞;多窗口之間消息傳遞;頁面與嵌套的iframe消息傳遞。

使用方法:postMessage(data,origin)方法接受兩個(gè)參數(shù)
data: html5規(guī)范支持任意基本類型或可復(fù)制的對(duì)象,但部分瀏覽器只支持字符串,所以傳參時(shí)最好用JSON.stringify()序列化。
origin: 協(xié)議+主機(jī)+端口號(hào),也可以設(shè)置為"*",表示可以傳遞給任意窗口,如果要指定和當(dāng)前窗口同源的話設(shè)置為"/"。

請(qǐng)求頁:http://www.domain1.com/a.html

<iframe id="iframe" src="http://www.domain2.com/b.html" style="display:none;"></iframe>
<script>
  var iframe = document.getElementById('iframe');    
  iframe.onload = function() { 
    var data = {           
      name: 'aym'        
    };        // 向domain2傳送跨域數(shù)據(jù) 
iframe.contentWindow.postMessage(JSON.stringify(data), 'http://www.domain2.com');    };    // 接受domain2返回?cái)?shù)據(jù) window.addEventListener('message', function(e) {       
   alert('data from domain2 ---> ' + e.data);   
 }, false);
</script>

接收頁:http://www.domain2.com/b.html

<script>   
 // 接收domain1的數(shù)據(jù)    
window.addEventListener('message', function(e) {        
  alert('data from domain1 ---> ' + e.data);        
  var data = JSON.parse(e.data);        
  if (data) {           
     data.number = 16;            // 處理后再發(fā)回domain1            
    window.parent.postMessage(JSON.stringify(data), 'http://www.domain1.com');        }    }, false);
</script>

該方法雖然直接通過window屬性解決跨域,但其適用場景有限,因此不推薦。

上集內(nèi)容到此結(jié)束。

總結(jié)一下:本篇主要帶大家弄清楚跨域的概念及跨域問題的來源,同時(shí)介紹了五種前端跨域問題的解決方案,但以上方案由于不同因素限制,所以均不推薦使用。下集內(nèi)容將講述前端最常用的幾種跨域解決方案,千萬不要錯(cuò)過。

我是小郭,想了解更多前端知識(shí)歡迎關(guān)注公眾號(hào)“一郭鮮”,小郭將帶你在前端海洋里馳騁。另外,文章也將同步更新到公眾號(hào),謝謝大家關(guān)注。

一郭鮮

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 跨域資源共享 CORS 對(duì)于web開發(fā)來講,由于瀏覽器的同源策略,我們需要經(jīng)常使用一些hack的方法去跨域獲取資源...
    默默先生Alec閱讀 666評(píng)論 0 0
  • 1. 什么是跨域 跨域,是指瀏覽器不能執(zhí)行其他網(wǎng)站的腳本。它是由瀏覽器的同源策略造成的,是瀏覽器對(duì)JavaScri...
    cbw100閱讀 6,486評(píng)論 2 86
  • 前言 原文地址:前端跨域總結(jié)博主博客地址:Damonare的個(gè)人博客 正文 1. 什么是跨域? 跨域一詞從字面意思...
    yo_yo_閱讀 537評(píng)論 0 5
  • 什么是跨域 跨域,是指瀏覽器不能執(zhí)行其他網(wǎng)站的腳本。它是由瀏覽器的同源策略造成的,是瀏覽器對(duì)JavaScript實(shí)...
    Yaoxue9閱讀 1,413評(píng)論 0 6
  • 曾經(jīng) 偶然的遇見 帶來那么多執(zhí)念 時(shí)間消逝 你慢慢的淡出 在我的視線 淡忘在 我的心里面 本以為 不會(huì)再相見 想不...
    時(shí)光向前閱讀 212評(píng)論 0 4

友情鏈接更多精彩內(nèi)容