Web前端入門第 89 問:總結 8 種跨域通信處理方案
為什么會跨域?跨域是誰附加的限制?為什么 APP 不會有跨域問題?
首先跨域問題是由于瀏覽器的同源策略(Same-Origin Policy)導致的,基本上所有瀏覽器都有限制,默認情況是不允許跨域訪問的!!
APP 的請求不受瀏覽器的同源策略限制,所以不存在跨域。類似一個服務器像另一個服務器發起請求一樣,也不會受跨域影響。
想想一下:如果瀏覽器沒有同源策略限制,A 網站可以隨意訪問 B 網站內容,那么現在 BAT 這些一線大廠還有護城河嗎?所有網站的數據都無隱私可言了,各種釣魚網站在瀏覽器中橫飛!!那世道...簡直太美~~
什么是同源
同源:指的是協議、域名和端口都相同。任意一個不同,都會觸發瀏覽器的同源策略,從而導致跨域。
以 MDM 的一個文檔地址為例,看看 URL 不同的組成部分:https://developer.mozilla.org:443/zh-CN/docs/Web/JavaScript?a=b#hash

跨域解決方案
雖然默認情況下瀏覽器是不允許跨域訪問的,但通過一些配置手段,還是能夠實現資源共享~~
1、跨域資源共享 CORS
目前主流的跨域共享方案,由服務器配置響應頭告訴瀏覽器是否允許跨域訪問:
// 或 * 表示所有源都可以訪問
Access-Control-Allow-Origin: https://domain.com
// 允許的方法
Access-Control-Allow-Methods: GET, POST, OPTIONS
// 允許的自定義頭
Access-Control-Allow-Headers: Content-Type, Authorization
// 允許攜帶 Cookie
Access-Control-Allow-Credentials: true
2、反向代理
原理就是前端請求同源服務器,由同源服務器向跨域目標發起請求,再由同源服務器返回結果給前端。繞過了瀏覽器同源策略,但需要服務器支持,如果請求量太大,對自己的服務器要求很高。
比如 nginx / node 中間件 / 開發環境的 dev-server 都是這種方式,以 nginx 跨域配置為例:
location /api/ {
proxy_pass https://domain.com/; # 需要請求的跨域目標
proxy_set_header Host $host;
}
3、WebSocket
WebSocket 是 HTML5 新增的協議,允許瀏覽器和服務器之間進行全雙工通信,天然支持跨域訪問。由于是雙向通信,所以對服務器壓力也不小。
const ws = new WebSocket('wss://domain.com');
ws.onmessage = (event) => console.log(event.data);
4、JSONP
利用 <script> 標簽,向目標服務器發起請求,目標服務器需要返回一段函數調用,將數據返回給前端。缺點是僅支持 get 請求,還容易引發 XSS 攻擊!
function handleResponse(data) {
console.log(data);
}
const script = document.createElement('script');
script.src = 'https://domain.com/data?callback=handleResponse';
document.head.appendChild(script);
https://domain.com/data?callback=handleResponse 需要返回 JS 代碼調用函數執行:
handleResponse({ data: 'hello' });
5、postMessage
此方式一般多用于 iframe 的跨域通信,比如 A 網頁使用 iframe 嵌入 B 網頁,這種情況就可以使用 postMessage 通信:
發送者:
// 發送方
iframe.contentWindow.postMessage('data', 'https://target-domain.com');
接收者:
// 接收方
window.addEventListener('message', (event) => {
if (event.origin !== 'https://source-domain.com') {
return;
}
console.log(event.data);
});
不推薦的方案
瀏覽器的版本升級后,一些老舊的跨域方案被棄用,比如:
6、document.domain + iframe
在過去,如果同一個主域名,子域名不同的情況,比如:a.domain.com 和 b.domain.com 之間進行通信,可以通過設置 document.domain = 'domain.com' 來解決,但現在的瀏覽器已經限制使用了!!
7、window.name + iframe
此方案有一些復雜,需要一個空白的同源頁面用于繞過瀏覽器的同源策略,然后獲取 iframe 的 name 屬性值,此處有大小限制,最多 2MB 的數據。
流程:
源頁面A (domainA.com)
↓ 創建iframe指向代理頁面B (domainB.com)
代理頁面B (domainB.com)
↓ 接收數據并存入 window.name
↓ 跳轉至與A同源的空白頁面C (domainA.com)
源頁面A
↓ 訪問iframe的window.name獲取數據
流程圖:

目前項目開發基本上已經不在使用這種方式,畢竟繞來繞去的,還不如一個 postMessage 跨域方案簡單。
8、location.hash + iframe
通過修改 URL Hash 實現父子 iframe 間單向數據傳輸,雖然勉強也能算作一種跨域方案,但由于 URL 的長度限制,數據量也不能太大,實際使用中也不簡單,所以項目上也很難見到它的身影~~
流程圖:

寫在最后
除了文章中這 8 種跨域方案外,還有一些單向數據通信的方法,比如說:
1、使用 Fetch API 的 no-cors 模式。
2、利用圖片的 src 屬性發起 GET 請求。
3、使用 sendBeacon 發送分析數據。
這些方法都只能向服務器發送數據,沒辦法獲得服務器的響應,所以一般多用于一些數據統計,比如:百度統計、谷歌分析等等。
當然也有一些歪門邪道,比如說:修改瀏覽器的配置允許跨域,編寫瀏覽器插件支持跨域等等。

浙公網安備 33010602011771號