前後端數據交互(七)——前端跨域解決方案(全)
一、什麼是跨域?
跨域就是非同源策略請求。
1.1、什麼是同源策略?
同源策略(SOP)是一種約定,是瀏覽器最核心的也是最基本的安全功能,如果缺少了同源策略,瀏覽器很容易受到 XSS、CSFR等攻擊。
協議+域名+埠號,三者都相同時就是同源,只要有一個不同就是跨域。
1.2、為什麼會產生跨域?
很久以前,前端一般只是切圖,將設計圖實現成靜態網頁,然後交給後端程式設計師,後端負責數據交互,將後端和前端程式碼混合開發。前端和後端是僅僅聯繫在一起,不便於開發和維護,後來逐步實現前後端分離,把伺服器拆分成三部分:
- WEB 伺服器:存放靜態資源
- 後台伺服器:提供業務邏輯和數據分析。
- 圖片伺服器
此時就出現請求跨域問題了。
二、跨域解決方案
跨域解決方案總共有 9 種,它們分別為:
- 通過 JSONP 跨域
- CORS 跨域資源共享
- http proxy 代理
- nginx 反向代理
- postMessage 跨域
- Websocket
- iframe + document.domain
- iframe + window.name
- iframe + location.hash
三、具體實現方案
3.1、JSONP
html頁面開發時,我們經常會使用 script、img、link、iframe 標籤引入對應的資源,我們發現它們有個共同特點就是可以引入任意域名下的資源,不存在跨域問題。因此我們利用 script 的特點,創建一個帶網址的跨域通訊。
具體的實現過程如圖:
原生請求程式碼為例如下:
// 注意 -- 函數聲明放前邊 <script> function back(res){ console.log(res)//返回數據 } </script> <script src="//127.0.0.1:3000/login?user='111'&callback=back"></script> node服務程式碼為: var querystring = require('querystring'); var http = require('http'); var server = http.createServer(); server.on('request', function(req, res) { console.log(req.url.split) var params = querystring.parse(req.url.split('?')[1]); console.log('params',params) var fn = params.callback; // jsonp返回設置 res.writeHead(200, { 'Content-Type': 'text/javascript' }); res.write(fn + '(' + JSON.stringify(params) + ')'); res.end(); }); server.listen('3000');
運行服務執行成功之後就會列印出: { callback: "back" user: "'111'" }
JSONP 的缺點:只允許處理 get 請求,由於請求的數據都暴露在url中,容易被劫持,安全性很差,所以不推薦使用。
3.2、CORS 跨域資源共享
通過設置響應頭處理的,需要後台配合處理。如果只是普通跨域請求,前端無需設置,後台設置
Access-Control-Allow-Origin,如:
/* 允許所有域名訪問 */ response.setHeader("Access-Control-Allow-Origin","*"); /* 只允許某個域名訪問 */ header("Access-Control-Allow-Origin: www.xxx.xom");
如果需要帶cookie請求,前端也需要設置欄位。如:
// 前端設置是否帶cookie xhr.withCredentials = true;
特點:客戶端發送(ajax fetch)請求,後台設置請求頭相關資訊,允許哪些源請求數據,需要處理 options 試探性請求。
3.3、proxy 代理
因為伺服器間的數據交互沒有跨域限制,所以通過一個中間代理伺服器請求目標伺服器的數據,也就是前端伺服器發送請求到代理伺服器,代理伺服器再請求目標伺服器,將數據返回給前端伺服器。
我們現在常用的三方框架 VUE、React項目中跨域解決方案都使用的是代理。如config.js中代理配置如下:
proxy: { //配置跨域 '/': { target: '//www.xxxx.com/', //線上 changOrigin: true, //允許跨域 pathRewrite: { '^/': '' } }, }
3.4、nginx反向代理
nginx反向代理,只需要後台配置服務就可以了,前端無需任何操作。其原理是:瀏覽器將請求發送到反向代理伺服器,由反向代理伺服器去選擇目標伺服器獲取數據,再返回給瀏覽器,此時暴露的是代理伺服器的地址,隱藏了真實的伺服器地址。
3.5、postMessage
postMessage 是全局對象 window 的屬性之一。可以安全地實現跨域通訊。通常,對於兩個不同頁面的腳本,只有當執行它們的頁面位於具有相同的協議(通常為https),埠號(443為https的默認值),以及主機 (兩個頁面的模數 Document.domain設置為相同的值) 時,這兩個腳本才能相互通訊。window.postMessage() 方法提供了一種受控機制來規避此限制,只要正確地使用,這種方法就很安全。
使用語法:
window.postMessage(data,url)
具體實現案例如下:
// a頁面 -- //localhost:1000/a.html <iframe id="iframe" src="//localhost:2000/b.html" style="display:none;"></iframe> <script> var iframe = document.getElementById('iframe'); iframe.onload = function() { // 向 b 傳送跨域數據 iframe.contentWindow.postMessage('傳輸數據', '//localhost:2000'); }; // 接受 b 返回數據 window.addEventListener('message', function(e) { alert('接收返回數據 ---> ' + e.data); }, false); </script> // b頁面 -- //localhost:2000/b.html <script> // 接收 a 的數據 window.addEventListener('message', function(e) { alert('data from domain1 ---> ' + e.data); var data = JSON.parse(e.data); if (data) { data.number = 16; // 處理後再發回domain1 window.parent.postMessage(JSON.stringify(data), '//localhost:1000/'); } }, false); </script>
3.6、webSocket
Websocket 是 html5一種新協議,實現瀏覽器與伺服器互相通訊,同時還允許跨域通訊。原生的 Websocket 可點擊 《WebSocket 基礎教程》學習更多知識。websocket api使用起來不便,我們更多地使用它的封裝版 socket.io,使用簡單,易操作。
具體的實現方法,請點擊《一文讀懂 WebSocket 原理》。
3.7、iframe + document.domain
只能實現同一個主域,不同子域之間的跨域通訊。
實現原理:兩個頁面都通過 js 強制設置 document.domain為基礎主域,就實現了同域。
缺點:限制較多。可作為了解就行。
3.8、iframe + location.hash
實現原理:a與b跨域通訊,通過中間頁c來實現,三個頁面,不同域之間可以利用 iframe 的 location.hash傳值,相同域之間直接利用 js 通訊。
必須要 3 個頁面。
具體實現:A域:a.html -> B域:b.html -> A域:c.html,a與b不同域只能通過hash值單向通訊,b與c也不同域也只能單向通訊,但c與a同域,所以c可通過parent.parent訪問a頁面所有對象。
3.9、iframe + window.name
與上3.8類似,也需要3個頁面。
實現原理:a.html 和 proxy.html必須在一個源內,b.html在另外一個源內,a與b實現跨域通訊,就是藉助 第三個 proxy.html 頁面,先把地址重新指向到同源中。proxy.html是一個空頁面,什麼也不用處理。