前後端數據交互(七)——前端跨域解決方案(全)

一、什麼是跨域?

跨域就是非同源策略請求。

1.1、什麼是同源策略?

同源策略(SOP)是一種約定,是瀏覽器最核心的也是最基本的安全功能,如果缺少了同源策略,瀏覽器很容易受到 XSS、CSFR等攻擊。

協議+域名+埠號,三者都相同時就是同源,只要有一個不同就是跨域。

1.2、為什麼會產生跨域?

很久以前,前端一般只是切圖,將設計圖實現成靜態網頁,然後交給後端程式設計師,後端負責數據交互,將後端和前端程式碼混合開發。前端和後端是僅僅聯繫在一起,不便於開發和維護,後來逐步實現前後端分離,把伺服器拆分成三部分:

  • WEB 伺服器:存放靜態資源
  • 後台伺服器:提供業務邏輯和數據分析。
  • 圖片伺服器

此時就出現請求跨域問題了。

二、跨域解決方案

跨域解決方案總共有 9 種,它們分別為:

  1. 通過 JSONP 跨域
  2. CORS 跨域資源共享
  3. http proxy 代理
  4. nginx 反向代理
  5. postMessage 跨域
  6. Websocket
  7. iframe + document.domain
  8. iframe + window.name
  9. 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是一個空頁面,什麼也不用處理。