日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

一网打尽,最全面的跨域解决方案来了!

發(fā)布時間:2025/3/20 编程问答 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 一网打尽,最全面的跨域解决方案来了! 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

今日推薦

這 9 個 Java 開源項目 yyds,你知道幾個?阿里技術專家推薦的20本書,免費送!K8S 部署 SpringBoot 項目(一篇夠用)妙用Java 8中的 Function接口 消滅if...else(非常新穎的寫法) Nginx 入門到實戰(zhàn),新手必懂。

今天我們來聊一個老生常談的話題,跨域!又是跨域,煩不煩 ?網上跨域的文章那么多,跨的我眼睛都疲勞了,不看了不看了 🤣 別走...

我盡量用最簡單的方式將常見的幾種跨域解決方案給大家闡釋清楚,相信認真看完本文,以后不管是作為受試者還是面試官,對于這塊的知識都能夠游刃有余。

什么是“跨源”

不是講跨域嗎 ?怎么又來個“跨源” ?字都能打錯的 ? 😄...稍安勿躁,其實我們平常說的跨域是一種狹義的請求場景,簡單來說,就是“跨“過瀏覽器的同源策略去請求資“源”,所以我們叫它“跨源”也沒啥問題。那么,跨源,源是什么?瀏覽器的同源策略什么是同源?協(xié)議,域名,端口都相同就是同源干巴巴的,能不能舉個栗子?栗子:),有的有的

const?url?=?'https://www.google.com:3000'

比如上面的這個 URL,協(xié)議是:https,域名是 www.google.com,端口是?3000。不同源了會怎么樣?會有很多限制,比如

  • Cookie,LocalStorage,IndexDB 等存儲性內容無法讀取

  • DOM 節(jié)點無法訪問

  • Ajax 請求發(fā)出去了,但是響應被瀏覽器攔截了

我就想請求個東西,至于嗎,為什么要搞個這么個東西限制我?基于安全考慮,沒有它,你可能會遇到

  • Cookie劫持,被惡意網站竊取數(shù)據(jù)

  • 更容易受到 XSS,CSRF 攻擊

  • 無法隔離潛在惡意文件

  • ... ...

所以,得有。正是因為瀏覽器同源策略的存在,你的 Ajax 請求有可能在發(fā)出去后就被攔截了,它還會給你報個錯:

??Access?to?XMLHttpRequest?at?'xxx'?from?origin?'xxx'?has?been?block?by?CORS,??policy:?No?'Access-Control-Allow-Origin'?header?is?present?on?the?requested?resource.

這種發(fā)出去拿不到響應的感受,就像你在網上沖浪時,被一股神秘的東方力量限制了一樣:

圖片

非常難受,所以,我們接下來就來看看怎么用科學的方法解決跨域的問題。

JSONP

這玩意兒就是利用了?<script>?標簽的?src 屬性沒有跨域限制的漏洞,讓我們可以得到從其他來源動態(tài)產生的 JSON 數(shù)據(jù)。為什么叫 JSONP ?JSONP 是 JSON with Padding 的縮寫,額,至于為什么叫這個名字,我網上找了下也沒個標準的解釋,還望評論區(qū)的各位老哥知道的趕緊告訴我: )怎么實現(xiàn) ?具體實現(xiàn)思路大致分為以下步驟

  • 本站的腳本創(chuàng)建一個 元素,src 地址指向跨域請求數(shù)據(jù)的服務器

  • 提供一個回調函數(shù)來接受數(shù)據(jù),函數(shù)名可以通過地址參數(shù)傳遞進行約定

  • 服務器收到請求后,返回一個包裝了 JSON 數(shù)據(jù)的響應字符串,類似這樣:callback({...})

瀏覽器接受響應后就會去執(zhí)行回調函數(shù) callback,傳遞解析后的 JSON 對象作為參數(shù),這樣我們就可以在 callback 里處理數(shù)據(jù)了。實際開發(fā)中,會遇到回調函數(shù)名相同的情況,可以簡單封裝一個 JSONP 函數(shù):

function?jsonp({?url,?params,?callback?})?{??return?new?Promise((resolve,?reject)?=>?{??//?創(chuàng)建一個臨時的?script?標簽用于發(fā)起請求??const?script?=?document.createElement('script');??//?將回調函數(shù)臨時綁定到?window?對象,回調函數(shù)執(zhí)行完成后,移除?script?標簽??window[callback]?=?data?=>?{??resolve(data);??document.body.removeChild(script);??};??//?構造?GET?請求參數(shù),key=value&callback=callback??const?formatParams?=?{?...params,?callback?};??const?requestParams?=?Object.keys(formatParams)??.reduce((acc,?cur)?=>?{??return?acc.concat([`${cur}=${formatParams[cur]}`]);??},?[])??.join('&');??//?構造?GET?請求的?url?地址??const?src?=?`${url}?${requestParams}`;??script.setAttribute('src',?src);??document.body.appendChild(script);??});?? }??//?調用時?? jsonp({??url:?'https://xxx.xxx',??params:?{...},??callback:?'func',?? })

我們用?Promise 封裝了請求,使異步回調更加優(yōu)雅,但是別看樓上的洋洋灑灑寫了一大段,其實本質上就是:

<script?src='https://xxx.xxx.xx?key=value&callback=xxx'><script>

想要看例子 ?戳這里JSONP 的優(yōu)點是簡單而且兼容性很好,但是缺點也很明顯,需要服務器支持而且只支持 GET 請求,下面我們來看第二種方案,也是目前主流的跨域解決方案,劃重點!😁

CORS

CORS[4](Cross-Origin Resource Sharing)的全稱叫?跨域資源共享,名稱好高大上,別怕,這玩意兒其實就是一種機制。瀏覽器不是有同源策略吶,這東西好是好,但是對于開發(fā)人員來說就不怎么友好了,因為我們可能經常需要發(fā)起一個?跨域 HTTP 請求。我們之前說過,跨域的請求其實是發(fā)出去了的,只不過被瀏覽器給攔截了,因為不安全,說直白點兒就是,你想要從服務器哪兒拿個東西,但是沒有經過人家允許啊。所以怎么樣才安全 ?服務器允許了不就安全了,這就是 CORS 實現(xiàn)的原理:使用額外的 HTTP 頭來告訴瀏覽器,讓運行在某一個 origin 上的 Web 應用允許訪問來自不同源服務器上的指定的資源

兼容性

目前,所有的主流瀏覽器都支持 CORS,其中,IE 瀏覽器的版本不能低于 10,IE 8 和 9 需要通過 XDomainRequest 來實現(xiàn)。完整的兼容性情況 ?

實現(xiàn)原理

CORS 需要瀏覽器和服務器同時支持,整個 CORS 的通信過程,都是瀏覽器自動完成。怎么個自動法??簡單來說,瀏覽器一旦發(fā)現(xiàn)請求是一個跨域請求,首先會判斷請求的類型,如果是簡單請求,會在請求頭中增加一個?Origin?字段,表示這次請求是來自哪一個。而服務器接受到請求后,會返回一個響應,響應頭中會包含一個叫?Access-Control-Allow-Origin?的字段,它的值要么包含由 Origin 首部字段所指明的域名,要么是一個 "*",表示接受任意域名的請求。如果響應頭中沒有這個字段,就說明當前源不在服務器的許可范圍內,瀏覽器就會報錯:

GET?/cors?HTTP/1.1?? Origin:?https://xxx.xx?? Accept-Language:?en-US?? Connection:?keep-alive?? ...?...

如果是非簡單請求,會在正式通信之前,發(fā)送一個預檢請求(preflight),目的在于詢問服務器,當前網頁所在的域名是否在服務器的許可名單之中,以及可以使用哪些 HTTP 動詞和頭信息字段,只有得到肯定答復,瀏覽器才會發(fā)出正式的請求,否則就報錯。你可能發(fā)現(xiàn)我們在日常的開發(fā)中,會看到很多使用 OPTION 方法發(fā)起的請求,它其實就是一個預檢請求:

OPTIONS?/cors?HTTP/1.1?? Origin:?http://xxx.xx?? Access-Control-Request-Method:?PUT?? Accept-Language:?en-US?? ...?...

那么到底哪些是簡單請求,哪些是非簡單請求 ?

請求類型

不會觸發(fā) CORS 預檢的,就是簡單請求。哪些請求不會觸發(fā)預檢 ?使用以下方法之一:GET, HEAD, POST,并且?Content-Type?的值僅限于下列三者之一:

  • text/plain

  • multipart/form-data

  • application/x-www-form-urlencoded

相反,不符合上述條件的就是非簡單請求啦。所以,實現(xiàn) CORS 的關鍵是服務器,只要服務器實現(xiàn)了 CORS 的相關接口,就可以實現(xiàn)跨域。CORS 與 JSONP相比,優(yōu)勢是支持所有的請求方法,缺點是兼容性上較 JSONP 差。除了 JSONP 和 CORS 外,還有一種常用的跨域解決方案:PostMessage,它更多地用于窗口間的消息傳遞。

PostMessage

PostMessage 是 Html5 XMLHttpRequest Level 2 中的 API,它可以實現(xiàn)跨文檔通信(Cross-document messaging)。兼容性上,IE8+,Chrome,Firfox 等主流瀏覽器都支持,可以放心用😊,如何理解跨文檔通信?你可以類比設計模式中的發(fā)布-訂閱模式,在這里,一個窗口發(fā)送消息,另一個窗口接受消息,之所以說類似發(fā)布-訂閱模式,而不是觀察者模式,是因為這里兩個窗口間沒有直接通信,而是通過瀏覽器這個第三方平臺。

window.postMessage(message,?origin,?[transfer])

postMessage 方法接收三個參數(shù),要發(fā)送的消息,接收消息的源和一個可選的 Transferable 對象,如何接收消息 ?

window.addEventListener("message",?function?receiveMessage(event)?{},?false);?//?推薦,兼容性更好??window.onmessage?=?function?receiveMessage(event)?{}?//?不推薦,這是一個實驗性的功能,兼容性不如上面的方法

接收到消息后,消息對象 event 中包含了三個屬性:source,origin,data,其中 data 就是我們發(fā)送的 message。此外,除了實現(xiàn)窗口通信,postMessage 還可以同 Web Worker 和 Service Work 進行通信。

Websocket

Websocket 是 HTML5 的一個持久化的協(xié)議,它實現(xiàn)了瀏覽器與服務器的全雙工通信,同時也是跨域的一種解決方案。什么是全雙工通信 ?簡單來說,就是在建立連接之后,server 與 client 都能主動向對方發(fā)送或接收數(shù)據(jù)。原生的 WebSocket API 使用起來不太方便,我們一般會選擇自己封裝一個 Websocket(嗯,我們團隊也自己封了一個 : ))或者使用已有的第三方庫,我們這里以第三方庫 ws 為例:

const?WebSocket?=?require('ws');??const?ws?=?new?WebSocket('ws://www.host.com/path');??ws.on('open',?function?open()?{??ws.send('something');?? });??ws.on('message',?function?incoming(data)?{??console.log(data);?? });?? ...?...

需要注意的是,Websocket 屬于長連接,在一個頁面建立多個 Websocket 連接可能會導致性能問題。

Nginx 反向代理

我們知道同源策略限制的是:瀏覽器向服務器發(fā)送跨域請求需要遵循的標準,那如果是服務器向服務器發(fā)送跨域請求呢?答案當然是,不受瀏覽器的同源策略限制。利用這個思路,我們就可以搭建一個代理服務器,接受客戶端請求,然后將請求轉發(fā)給服務器,拿到響應后,再將響應轉發(fā)給客戶端:

圖片

這就是 Nginx 反向代理的原理,只需要簡單配置就可以實現(xiàn)跨域:

#?nginx.config?? #?...?? server?{??listen???????80;??server_name??www.domain1.com;??location?/?{??proxy_pass???http://www.domain2.com:8080;??#反向代理??proxy_cookie_domain?www.domain2.com?www.domain1.com;?#修改cookie里域名??index??index.html?index.htm;??#?當用?webpack-dev-server?等中間件代理接口訪問?nignx?時,此時無瀏覽器參與,故沒有同源限制,下面的跨域配置可不啟用??add_header?Access-Control-Allow-Origin?*;??add_header?Access-Control-Allow-Credentials?true;??#?...??}?? }

Node 中間件代理

實現(xiàn)的原理和我們前文提到的代理服務器原理如出一轍,只不過這里使用了 Node 中間件做為代理。需要注意的是,瀏覽器向代理服務器請求時仍然遵循同源策略,別忘了在 Node 層通過 CORS 做跨域處理:

const?https?=?require('https')?? //?接受客戶端請求?? const?sever?=?https.createServer((req,?res)?=>?{??...??const?{?method,?headers?}?=?req??//?設置?CORS?允許跨域??res.writeHead(200,?{??'Access-Control-Allow-Origin':?'*',??'Access-Control-Allow-Methods':?'*',??'Access-Control-Allow-Headers':?'Content-Type',??...??})??//?請求服務器??const?proxy?=?https.request({?host:?'xxx',?method,?headers,?...},?response?=>?{??let?body?=?''??response.on('data',?chunk?=>?{?body?=?body?+?chunk?})??response.on('end',?()?=>?{??//?響應結果轉發(fā)給客戶端??res.end(body)??})??})??//?結束請求??proxy.end()?? })

document.domain

二級域名相同的情況下,設置 document.domain 就可以實現(xiàn)跨域。什么是二級域名 ? a.test.com 和 b.test.com 就屬于二級域名,它們都是 test.com 的子域。如何實現(xiàn)跨域 ?

document.domain?=?'test.com'?//?設置?domain?相同??//?通過?iframe?嵌入跨域的頁面?? const?iframe?=?document.createElement('iframe')?? iframe.setAttribute('src',?'b.test.com/xxx.html')?? iframe.onload?=?function()?{??//?拿到?iframe?實例后就可以直接訪問?iframe?中的數(shù)據(jù)??console.log(iframe.contentWindow.xxx)?? }?? document.appendChild(iframe)

總結

當然,除了上述的方案外,比較?Hack?的還有:window.name, location.hash,但是這些跨域的方式現(xiàn)在我們已經不推薦了,為什么 ?因為相比之下有更加安全和強大的 PostMessage 作為替代。跨域的方案其實有很多,總結下來:

  • CORS 支持所有的 HTTP 請求,是跨域最主流的方案

  • JSONP 只支持 GET 請求,但是可以兼容老式瀏覽器

  • Node 中間件和 Nginx 反向代理都是利用了服務器對服務器沒有同源策略限制

  • Websocket 也是一種跨域的解決方案

  • PostMessage 可以實現(xiàn)跨文檔通信,更多地用于窗口通信

  • document.domain, window.name, location.hash 逐漸淡出歷史舞臺,作為替代 PostMessage 是一種不錯的方案

寫在最后

才疏學淺,難免有錯誤,文章有誤之處還望不吝指正!

來源:github.com/campcc/blog/issues/15

推薦文章1、一款高顏值的 SpringBoot+JPA 博客項目2、超優(yōu) Vue+Element+Spring 中后端解決方案3、推薦幾個支付項目!4、推薦一個 Java 企業(yè)信息化系統(tǒng)5、一款基于 Spring Boot 的現(xiàn)代化社區(qū)(論壇/問答/社交網絡/博客) 《新程序員》:云原生和全面數(shù)字化實踐50位技術專家共同創(chuàng)作,文字、視頻、音頻交互閱讀

總結

以上是生活随笔為你收集整理的一网打尽,最全面的跨域解决方案来了!的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。