跨域解決方案匯總

一、代理跨域

  • 場(chǎng)景1:你的項(xiàng)目myweb矾柜,myweb的前端有一個(gè)接口是去訪問一個(gè)非myweb的服務(wù)器窄刘。非myweb服務(wù)器是第三方服務(wù)器窥妇,你不能去對(duì)第三方服務(wù)器做改動(dòng)。

  • 場(chǎng)景2:你的項(xiàng)目是個(gè)微服務(wù)架構(gòu)的娩践。那你的前端頁面可能就需要去很多個(gè)服務(wù)器上訪問數(shù)據(jù)活翩。

原理解析:

  • 跨域請(qǐng)求報(bào)錯(cuò)歸根結(jié)底是瀏覽器禁止使用XHR對(duì)象向不同源的服務(wù)器地址發(fā)起HTTP請(qǐng)求。如果是服務(wù)器跨域向多個(gè)不同的服務(wù)器發(fā)送請(qǐng)求就不會(huì)有跨域問題存在翻伺。因此材泄,我們可以讓瀏覽器只向一個(gè)服務(wù)器方式請(qǐng)求,讓這個(gè)服務(wù)器代替瀏覽器去不同的服務(wù)器上請(qǐng)求資源再返回給瀏覽器吨岭。這個(gè)服務(wù)器就是代理服務(wù)器了拉宗。

  • 下面推薦一個(gè)常用代理服務(wù)器nginx。

  • 什么是nginx?

Nginx (engine x) 是一款輕量級(jí)的Web 服務(wù)器 辣辫、反向代理服務(wù)器及電子郵件(IMAP/POP3)代理服務(wù)器旦事。

  • 把ui所在的服務(wù)器和跨域服務(wù)器都用nginx代理轉(zhuǎn)發(fā),瀏覽器訪問nginx急灭,nginx到ui服務(wù)獲取ui姐浮,再把ui下載到瀏覽器,瀏覽器發(fā)起ui中的URL葬馋,該URL為Nginx封裝后的跨域服務(wù)器的URL或ui服務(wù)器的URL卖鲤,該URL到達(dá)Nginx之后,會(huì)被轉(zhuǎn)發(fā)到跨域服務(wù)器或ui服務(wù)器点楼,請(qǐng)求處理完畢后扫尖,會(huì)通過Nginx中轉(zhuǎn)返回給瀏覽器。暴露出來的或者瀏覽器所發(fā)起的url都是nginx的url掠廓,nginx去跨域服務(wù)器和ui服務(wù)器獲取響應(yīng)换怖,返給瀏覽器,這樣就沒有跨域問題了蟀瞧。

二沉颂、CORS

  • 場(chǎng)景:前后端分離的開發(fā)模式下,在本地進(jìn)行接口聯(lián)調(diào)時(shí):也許在你的項(xiàng)目里悦污,你想嘗試前后端分離的開發(fā)模式铸屉。

  • 你在本地開發(fā)時(shí),mock了一些假數(shù)據(jù)來幫助自己本地開發(fā)切端。而有一天彻坛,你希望在本地和后端同學(xué)進(jìn)行聯(lián)調(diào)。此時(shí),后端rd的接口地址和你發(fā)生了跨域問題昌屉。這阻止了你們的聯(lián)調(diào)钙蒙,你只能繼續(xù)使用你mock的假數(shù)據(jù)。

解決方案:

  • CORS需要瀏覽器和服務(wù)器同時(shí)支持间驮。如何支持躬厌?請(qǐng)看瀏覽器對(duì)跨域訪問的判定小節(jié)。

  • 整個(gè)CORS通信過程竞帽,都是瀏覽器自動(dòng)完成扛施,不需要用戶參與。對(duì)于開發(fā)者來說屹篓,CORS通信與同源的AJAX通信沒有差別疙渣,代碼完全一樣。瀏覽器一旦發(fā)現(xiàn)AJAX請(qǐng)求跨源抱虐,就會(huì)自動(dòng)添加一些附加的頭信息昌阿,有時(shí)還會(huì)多出一次附加的請(qǐng)求饥脑,但用戶不會(huì)有感覺恳邀。

  • 因此,實(shí)現(xiàn)CORS通信的關(guān)鍵是服務(wù)器灶轰。只要服務(wù)器實(shí)現(xiàn)了CORS接口谣沸,就可以跨源通信。

  • 服務(wù)器要給接口的響應(yīng)頭設(shè)置:Access-Control-Allow-Origin:*

三笋颤、jsonp

  • 場(chǎng)景:跨域發(fā)送get請(qǐng)求

  • jsonp解決跨域問題的本質(zhì): <script> 標(biāo)簽可以請(qǐng)求不同域名下的資源乳附,即 <script> 請(qǐng)求不受瀏覽器同源策略影響。

      //首先給body動(dòng)態(tài)添加一個(gè) <script>
      var script = document.createElement('script');
      script.setAttribute("type","text/javascript");
      script.src = 'http://example.com/ip?callback=foo';
      document.body.appendChild(script);

      function foo(data) {
        console.log('Your public IP address is: ' + data.ip);
      };
  • 上面的script會(huì)向 http://example.com/ 服務(wù)器發(fā)送請(qǐng)求伴澄,這個(gè)請(qǐng)求的url后面帶了個(gè)callback參數(shù)赋除,是用來告訴服務(wù)器回調(diào)方法的方法名的。因?yàn)榉?wù)器收到請(qǐng)求后非凌,會(huì)把相應(yīng)數(shù)據(jù)寫進(jìn)foo的參數(shù)位置举农,也就是說服務(wù)器會(huì)返回的腳本如下
foo({
      "ip": "8.8.8.8"
   });
  • 這樣瀏覽器通過<script>下載的資源就是上面的腳本了,<script>下載完成就會(huì)立即執(zhí)行敞嗡,也就是說http://example.com/ip?callback=foo這個(gè)請(qǐng)求返回后就會(huì)立即執(zhí)行上面的腳本代碼颁糟,而這個(gè)腳本代碼就是調(diào)用回調(diào)方法和拿到j(luò)son數(shù)據(jù)了。

四喉悴、document.domain跨域

  • 場(chǎng)景1:你的http://www.damonare.cn/a.html頁面里使用<iframe>調(diào)用另一個(gè)http://damonare.cn/b.html頁面棱貌。這時(shí)候你想在a頁面里獲取b頁面里的dom,然后進(jìn)行操作箕肃。然后你會(huì)發(fā)現(xiàn)你不能獲得b的dom婚脱。document.getElementById("myIFrame").contentWindow.document或window.parent.document.body因?yàn)閮蓚€(gè)窗口不同源而報(bào)錯(cuò)。

  • 解決方案:這時(shí)候你只需要在a頁面里和b頁面里把document.domain設(shè)置成相同的值就可以在兩個(gè)頁面里操作Dom了。

  • 場(chǎng)景2:你在http://www.damonare.cn/a.html頁面里寫入了document.cookie = "test1=hello";你在http://damonare.cn/b.html頁面是拿不到這個(gè)cookie的障贸。

  • 解決方案:Cookie 是服務(wù)器寫入瀏覽器的一小段信息涡贱,只有同源的網(wǎng)頁才能共享。但是惹想,兩個(gè)網(wǎng)頁一級(jí)域名相同问词,只是二級(jí)域名不同,瀏覽器允許通過設(shè)置document.domain共享 Cookie嘀粱。另外激挪,服務(wù)器也可以在設(shè)置Cookie的時(shí)候,指定Cookie的所屬域名為一級(jí)域名锋叨。這樣的話垄分,二級(jí)域名和三級(jí)域名不用做任何設(shè)置,都可以讀取這個(gè)Cookie娃磺。

  • 注意:

    1. document.domain限制:雖然可讀寫薄湿,但只能設(shè)置成自身或者是高一級(jí)的父域且主域必須相同。所以只能解決一級(jí)域名相同二級(jí)域名不同的跨域問題偷卧。
    2. document.domain只適用于 Cookie 和 iframe 窗口豺瘤,LocalStorage 和 IndexDB 無法通過這種方法跨域。

五听诸、window.name跨域

  • 場(chǎng)景1:現(xiàn)在瀏覽器的一個(gè)標(biāo)簽頁里打開http://www.damonare.cn/a.html頁面坐求,你通過location.href=http://baidu.com/b.html,在同一個(gè)瀏覽器標(biāo)簽頁里打開了不同域名下的頁面晌梨。這時(shí)候這兩個(gè)頁面你可以使用window.name來傳遞參數(shù)桥嗤。因?yàn)?a target="_blank">window.name指的是瀏覽器窗口的名字,只要瀏覽器窗口相同仔蝌,那么無論在哪個(gè)網(wǎng)頁里訪問值都是一樣的泛领。

  • 場(chǎng)景2:你的http://www.damonare.cn/a.html頁面里使用<iframe>調(diào)用另一個(gè)http://baidu.com/b.html頁面。這時(shí)候你想在a頁面里獲取b頁面里的dom敛惊,然后進(jìn)行操作渊鞋。然后你會(huì)發(fā)現(xiàn)你不能獲得b的dom。同樣會(huì)因?yàn)椴煌炊鴪?bào)錯(cuò)豆混,和上面提到的不同之處就是兩個(gè)頁面的一級(jí)域名也不相同篓像。這時(shí)候document.domain就解決不了了。

  • 解決方案:瀏覽器窗口有window.name屬性皿伺。這個(gè)屬性的最大特點(diǎn)是员辩,無論是否同源,只要在同一個(gè)窗口里鸵鸥,前一個(gè)網(wǎng)頁設(shè)置了這個(gè)屬性奠滑,后一個(gè)網(wǎng)頁可以讀取它丹皱。。比如你在b頁面里設(shè)定window.name="hello"宋税,你再返回到a頁面摊崭,在a頁面里訪問window.name,可以得到hello杰赛。

  • 這種方法的優(yōu)點(diǎn)是呢簸,window.name容量很大,可以放置非常長(zhǎng)的字符串乏屯;缺點(diǎn)是必須監(jiān)聽子窗口window.name屬性的變化根时,影響網(wǎng)頁性能。

六辰晕、postMessage方法跨域

  • 場(chǎng)景1:在a頁面里打開了另一個(gè)不同源的頁面b蛤迎,你想要讓a和b兩個(gè)頁面互相通信。比如含友,a要訪問b的LocalStorage替裆。

  • 場(chǎng)景2:你的a頁面里的iframe的src是不同源的b頁面,你想要讓a和b兩個(gè)頁面互相通信窘问。比如辆童,a要訪問b的LocalStorage。

  • 解決方案:HTML5y引入了一個(gè)全新的API南缓,跨文檔通信 API(Cross-document messaging)胸遇。這個(gè)API為window對(duì)象新增了一個(gè)window.postMessage方法,允許跨窗口通信汉形,不論這兩個(gè)窗口是否同源。a就可以把它的LocalStorage倍阐,發(fā)送給b概疆,b也可以把自己的LocalStorage發(fā)給a。

  • window.postMessage(message, targetOrigin, [transfer])峰搪,有三個(gè)參數(shù):

  1. message是向目標(biāo)窗口發(fā)送的數(shù)據(jù)岔冀;
  2. targetOrigin屬性來指定哪些窗口能接收到消息事件,其值可以是字符串"*"(表示無限制)或者一個(gè)URI(或者說是發(fā)送消息的目標(biāo)域名)概耻;
  3. transfer可選參數(shù)使套,是一串和message 同時(shí)傳遞的 Transferable 對(duì)象. 這些對(duì)象的所有權(quán)將被轉(zhuǎn)移給消息的接收方,而發(fā)送一方將不再保有所有權(quán)鞠柄。
  • 另外消息的接收方必須有監(jiān)聽事件侦高,否則發(fā)送消息時(shí)就會(huì)報(bào)錯(cuò)。
    • The target origin provided ('http://localhost:8080') does not match the recipient window's origin ('http://localhost:63343').

    • window.addEventListener("message",onmessage);onmessage接收到的message事件包含三個(gè)屬性:

  1. data:從其他 window 中傳遞過來的數(shù)據(jù)厌杜。
  2. origin:調(diào)用 postMessage 時(shí)消息發(fā)送方窗口的 origin 奉呛。請(qǐng)注意计螺,這個(gè)origin不能保證是該窗口的當(dāng)前或未來origin,因?yàn)閜ostMessage被調(diào)用后可能被導(dǎo)航到不同的位置瞧壮。
  3. source:對(duì)發(fā)送消息的窗口對(duì)象的引用; 您可以使用此來在具有不同origin的兩個(gè)窗口之間建立雙向通信登馒。
  • 例子:我在a頁面執(zhí)行
 var popup = window.open('http://localhost:3000', 'title');
 popup.postMessage('Hello World!', 'http://localhost:3000');`

同時(shí)在http://localhost:3000的頁面里監(jiān)聽message事件:

window.onload=function () {
      window.addEventListener("message",onmessage);
}
function onmessage(event) {
      if(event.origin=="http://localhost:63343"){//http://localhost:63343是發(fā)送方a的域名
          console.log(event.data);//'Hello World!'
      }
      console.log(event.data);//'Hello World!'
}

注意: 在 Gecko 6.0 (Firefox 6.0 / Thunderbird 6.0 / SeaMonkey 2.3)之前, 參數(shù) message 必須是一個(gè)字符串咆槽。 從 Gecko 6.0 (Firefox 6.0 / Thunderbird 6.0 / SeaMonkey 2.3)開始陈轿,參數(shù) message被使用結(jié)構(gòu)化克隆算法進(jìn)行序列化。這意味著您可以將各種各樣的數(shù)據(jù)對(duì)象安全地傳遞到目標(biāo)窗口秦忿,而不必自己序列化它們济欢。

七、location.hash跨域

  • location.hash就是指URL的#號(hào)后面的部分小渊。

  • 場(chǎng)景:
    父窗口和iframe的子窗口之間通訊或者是window.open打開的子窗口之間的通訊法褥。

  • 解決方案:
    父窗口改變子窗口的url的#號(hào)后面的部分,后者把要傳遞的參數(shù)寫在#后面酬屉,子窗口監(jiān)聽window.onhashchange事件半等,得到通知,讀取window.location.hash解析出有用的數(shù)據(jù)呐萨。同樣子窗口也可以向父窗口傳遞數(shù)據(jù)杀饵。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市谬擦,隨后出現(xiàn)的幾起案子切距,更是在濱河造成了極大的恐慌,老刑警劉巖惨远,帶你破解...
    沈念sama閱讀 216,402評(píng)論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件谜悟,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡北秽,警方通過查閱死者的電腦和手機(jī)葡幸,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來贺氓,“玉大人蔚叨,你說我怎么就攤上這事≌夼啵” “怎么了蔑水?”我有些...
    開封第一講書人閱讀 162,483評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)扬蕊。 經(jīng)常有香客問我搀别,道長(zhǎng),這世上最難降的妖魔是什么厨相? 我笑而不...
    開封第一講書人閱讀 58,165評(píng)論 1 292
  • 正文 為了忘掉前任领曼,我火速辦了婚禮鸥鹉,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘庶骄。我一直安慰自己毁渗,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,176評(píng)論 6 388
  • 文/花漫 我一把揭開白布单刁。 她就那樣靜靜地躺著灸异,像睡著了一般。 火紅的嫁衣襯著肌膚如雪羔飞。 梳的紋絲不亂的頭發(fā)上肺樟,一...
    開封第一講書人閱讀 51,146評(píng)論 1 297
  • 那天,我揣著相機(jī)與錄音逻淌,去河邊找鬼么伯。 笑死,一個(gè)胖子當(dāng)著我的面吹牛卡儒,可吹牛的內(nèi)容都是我干的田柔。 我是一名探鬼主播,決...
    沈念sama閱讀 40,032評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼骨望,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼硬爆!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起擎鸠,我...
    開封第一講書人閱讀 38,896評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤缀磕,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后劣光,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體袜蚕,經(jīng)...
    沈念sama閱讀 45,311評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,536評(píng)論 2 332
  • 正文 我和宋清朗相戀三年赎线,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了廷没。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,696評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡垂寥,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出另锋,到底是詐尸還是另有隱情滞项,我是刑警寧澤,帶...
    沈念sama閱讀 35,413評(píng)論 5 343
  • 正文 年R本政府宣布夭坪,位于F島的核電站文判,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏室梅。R本人自食惡果不足惜戏仓,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,008評(píng)論 3 325
  • 文/蒙蒙 一疚宇、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧赏殃,春花似錦敷待、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至抗蠢,卻和暖如春举哟,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背迅矛。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評(píng)論 1 269
  • 我被黑心中介騙來泰國打工妨猩, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人秽褒。 一個(gè)月前我還...
    沈念sama閱讀 47,698評(píng)論 2 368
  • 正文 我出身青樓壶硅,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國和親震嫉。 傳聞我的和親對(duì)象是個(gè)殘疾皇子森瘪,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,592評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容

  • 跨域問題的場(chǎng)景和解決方案多種多樣,只要是做前端開發(fā)票堵,總會(huì)遇到扼睬。而且面試時(shí)也是必問的問題。所以自己學(xué)習(xí)總結(jié)記錄一下悴势。...
    花開_陳鳳娟閱讀 737評(píng)論 0 0
  • 1. 什么是跨域 跨域窗宇,是指瀏覽器不能執(zhí)行其他網(wǎng)站的腳本。它是由瀏覽器的同源策略造成的特纤,是瀏覽器對(duì)JavaScri...
    cbw100閱讀 6,328評(píng)論 2 86
  • 什么是跨域 跨域军俊,是指瀏覽器不能執(zhí)行其他網(wǎng)站的腳本。它是由瀏覽器的同源策略造成的捧存,是瀏覽器對(duì)JavaScript實(shí)...
    他方l閱讀 1,063評(píng)論 0 2
  • 什么是跨域粪躬?跨域是指一個(gè)域下的文檔或腳本試圖去請(qǐng)求另一個(gè)域下的資源,這里跨域是廣義的昔穴。 廣義的跨域: 資源跳轉(zhuǎn): ...
    薛定諤的盯襠貓閱讀 1,278評(píng)論 1 38
  • 歐美刮了很久的me too運(yùn)動(dòng)終于刮到了中國镰官,最近幾天微博微信刷屏。 公共場(chǎng)合遭遇性騷擾吗货,放下恥感反擊 相信坐公交...
    mars1989閱讀 970評(píng)論 0 2