天天看點

終結同源政策!輕松實作跨域通路的9種方式!

作者:阿珊和她的貓
終結同源政策!輕松實作跨域通路的9種方式!

跨域指的是在Web浏覽器中,一個站點的腳本試圖向另一個站點的腳本發起HTTP請求,這種請求是由于浏覽器遵守同源政策(Same-Origin Policy)而被拒絕的。為了解決這個問題,我們可以使用以下9種方式:

1. JSONP:通過動态建立<script>标簽向其他域請求資料,并将資料作為回調函數參數傳遞回來。

當需要在一個網頁中通路另一個域名下的資料時,由于浏覽器的同源政策會阻止這種通路,是以就需要使用JSONP來實作跨域資料的通路。

JSONP(JSON with Padding)是一種實作跨域網絡資料傳輸的技術,它利用了<script>标簽的跨域通路特性。

使用JSONP的基本過程如下:
  1. 在用戶端代碼中定義一個全局的回調函數,用于接收伺服器傳回的資料。
  2. 建構一個動态建立

示例代碼如下:

function jsonpRequest(url, callback) {
  // 建立<script>标簽
  var script = document.createElement('script');
  // 建構URL位址,指定回調函數名稱
  var callbackName = 'jsonp_callback_' + Math.floor(Math.random() * 100000);
  url += '&callback=' + callbackName;
  // 在window對象上定義回調函數
  window[callbackName] = function(data) {
    // 處理伺服器傳回資料
    callback(data);
    // 删除回調函數
    delete window[callbackName];
    // 移除<script>标簽
    document.body.removeChild(script);
  };
  // 設定<script>标簽的src屬性
  script.src = url;
  // 添加<script>标簽到<body>元素中
  document.body.appendChild(script);
}
           

通過調用這個jsonpRequest函數,我們可以跨域請求資料,并将資料傳遞給回調函數進行處理。

2. CORS:在伺服器端設定響應頭資訊,允許跨域通路。

CORS(Cross-Origin Resource Sharing)是一種允許跨域通路資源的機制,它通過在伺服器端設定響應頭資訊來允許跨域通路。

使用CORS的基本過程如下:
  1. 在伺服器端設定響應頭資訊,允許指定的跨域請求通路資源。通過設定Access-Control-Allow-Origin頭資訊,指定允許通路的源,比如設定為"*"表示允許任意源通路。
  2. 浏覽器在發送跨域請求時,會在請求頭中添加Origin頭資訊,這個頭資訊訓示了請求的源位址。
  3. 伺服器在接收到請求後,會根據Origin頭資訊,在響應頭中添加Access-Control-Allow-Origin頭資訊,以表明該源位址被允許通路。

示例代碼如下:

// 使用Node.js的Express架構實作CORS跨域通路
const express = require('express');
const app = express();

// 設定允許跨域通路的資源
app.use((req, res, next) => {
  // 設定允許跨域的源
  res.setHeader('Access-Control-Allow-Origin', '*');
  // 設定允許跨域的請求頭
  res.setHeader('Access-Control-Allow-Headers', 'origin, content-type, accept');
  // 設定允許跨域的請求方法
  res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE');
  // 繼續處理請求
  next();
});

// 處理API請求
app.get('/api', (req, res) => {
  // 傳回JSON資料
  res.json({
    code: 0,
    message: 'success',
    data: {
      name: 'JSONP',
      description: '使用JSONP實作跨域資料通路'
    }
  });
});

// 啟動伺服器
app.listen(3000, () => {
  console.log('Server is running on port 3000');
});
           

在這個示例代碼中,我們使用了Node.js的Express架構實作了一個簡單的API,該API允許跨域通路,通過設定響應頭資訊,指定了允許跨域請求通路的源、請求頭和請求方法。用戶端代碼可以使用XMLHttpRequest或Fetch API來通路該API,并獲得傳回的JSON資料。

3. WebSocket:采用全雙工通信方式,無需提前建立HTTP連接配接,可以跨域通訊。

WebSocket是一種基于TCP協定實作的全雙工通信協定,它采用了一種類似HTTP的握手機制,在建立連接配接後,雙方可以進行自由的資料傳輸,可以跨域通信,無需提前建立HTTP連接配接。

使用WebSocket的基本過程如下:
  1. 用戶端通過WebSocket對象建立WebSocket連接配接,指定WebSocket服務端的位址。
  2. WebSocket服務端接收到連接配接請求後,進行握手處理,并建立起連接配接。在握手時,服務端會告知用戶端是否支援WebSocket協定。
  3. 如果握手成功,用戶端和服務端就可以進行雙向資料傳輸,用戶端和服務端均可發送和接收資料。

示例代碼如下:

// 建立WebSocket連接配接
var socket = new WebSocket('ws://localhost:8080');

// 連接配接成功時,列印日志資訊
socket.onopen = function() {
  console.log('WebSocket connection is opened.');
};

// 接收到消息時,列印消息内容
socket.onmessage = function(event) {
  console.log('WebSocket received message:', event.data);
};

// 連接配接關閉時,列印日志資訊
socket.onclose = function() {
  console.log('WebSocket connection is closed.');
};

// 發送消息
socket.send('Hello WebSocket!');
           

在這個示例中,我們使用WebSocket對象建立了一個WebSocket連接配接,并指定了WebSocket服務端的位址,然後通過事件監聽函數來處理連接配接狀态、接收消息和發送消息。用戶端和服務端之間可以發送和接收資料,實作實時通信的功能。由于WebSocket協定采用了類似HTTP的握手機制,是以WebSocket可以跨域通信,無需提前建立HTTP連接配接。

4.postMessage:HTML5提供了一個專門用于跨文檔通信的API,可以跨域通信。

HTML5提供了一個專門用于跨文檔通信的API——postMessage。postMessage方法可以在不同的視窗(或文檔)之間傳遞資料,包括字元串和對象等資料類型,可以實作跨域通信。

使用postMessage的基本過程如下:
  1. 在發送消息的視窗中,使用postMessage方法向指定的視窗發送消息,指定視窗的origin屬性(協定、域名、端口号)以便接收視窗驗證消息是否來自信任的源。
  2. 在接收消息的視窗中,通過添加message事件監聽函數來接受消息,事件對象包含了發送視窗的資訊、消息資料等。

示例代碼如下:

// 發送消息
var sendMessage = {
  type: 'text',
  content: 'Hello, postMessage!'
};
var targetWindow = window.opener;
var targetOrigin = '*';
targetWindow.postMessage(sendMessage, targetOrigin);

// 接收消息
window.addEventListener('message', function(event) {
  if (event.origin !== 'http://example.com') {
    return;
  }
  console.log(event.data);
}, false);
           

在這個示例中,我們在發送消息的視窗中,使用postMessage方法向指定的視窗發送消息,指定了接收視窗的origin,以保證消息來自可信任的源。在接收消息的視窗中,添加message事件監聽函數來接收消息,通過驗證消息來自可信任的源,然後使用事件對象來擷取消息資料。通過這種方式實作了從一個視窗向另一個視窗發送跨域消息,并實作了跨域通信。

5. 代理伺服器:通過在伺服器端代理請求,将跨域請求轉到同一伺服器,再傳回到請求端。

代理伺服器是指在伺服器端轉發請求和響應的中間層伺服器,它可以代理用戶端向目标伺服器發送請求,擷取響應,進而實作跨域請求。

使用代理伺服器實作跨域請求的基本過程如下:
  1. 用戶端向代理伺服器發送請求。
  2. 代理伺服器接收到請求後,将請求轉發給目标伺服器,擷取響應。
  3. 代理伺服器将響應傳回給用戶端,完成跨域請求。

示例代碼如下:

// 在Node.js中使用http-proxy中間件實作代理伺服器
const http = require('http');
const httpProxy = require('http-proxy');

// 建立代理伺服器
const proxy = httpProxy.createProxyServer({});

// 處理請求
const server = http.createServer((req, res) => {
  console.log('Proxy request:', req.url);
  // 設定響應頭,允許跨域通路
  res.setHeader('Access-Control-Allow-Origin', '*');
  res.setHeader('Access-Control-Allow-Headers', 'origin, content-type, accept');
  // 如果是API請求,将請求轉發到目标伺服器
  if (req.url.startsWith('/api')) {
    proxy.web(req, res, {
      target: 'http://localhost:3000'
    });
  } else {
    res.end();
  }
});

// 監聽端口,啟動伺服器
server.listen(8000, () => {
  console.log('Server is running on port 8000');
});
           

在這個示例中,我們使用Node.js自帶的http子產品建立了一個HTTP伺服器,通過設定響應頭資訊,實作了允許跨域通路。在處理請求時,我們通過判斷請求URL,将API請求轉發到目标伺服器,使用http-proxy中間件實作了代理伺服器的功能,将請求轉發到同一伺服器上,再将響應傳回給用戶端,進而實作了跨域請求的目的。

6. nginx反向代理:利用nginx的反向代理特性來實作跨域。

Nginx是一款高性能的Web伺服器和反向代理伺服器,通過配置nginx的反向代理功能,可以解決跨域問題。

使用nginx反向代理實作跨域請求的基本步驟如下:
  1. 安裝nginx,并編輯配置檔案。
  2. 在nginx配置檔案中,使用location指令比對需要代理的URL,并設定代理伺服器的位址和端口号。
  3. 重新開機nginx,使配置檔案生效。

示例代碼如下:

# 将請求 /api/ 轉發到 http://localhost:3000/
location /api/ {
  # 設定允許跨域通路
  add_header 'Access-Control-Allow-Origin' '*';
  
  # 設定需要代理的目标伺服器
  proxy_pass http://localhost:3000/;
}
           

在這個示例中,我們使用nginx反向代理功能,将請求URL為/api/的請求轉發到http://localhost:3000/伺服器,實作了跨域請求。同時,我們在nginx配置檔案中設定了Access-Control-Allow-Origin頭資訊,允許跨域通路,進而實作了跨域通信的目的。

7. iframe嵌套:利用iframe的跨域通信特性,在不同的iframe之間實作資料交換。

iframe是HTML中的一個重要元素,可以在一個網頁中嵌入另一個網頁或文檔。iframe具有通路不同域名網頁的能力,是以可以在不同iframe之間實作跨域通信。

使用iframe嵌套實作跨域通信的基本過程如下:
  1. 在首頁面中定義一個iframe元素,并設定src屬性為跨域頁面的URL。
  2. 在跨域頁面中,通過window.parent來擷取首頁面的window對象,并向首頁面發送消息。
  3. 在首頁面中,通過監聽message事件來接收來自iframe的消息。

示例代碼如下:

在首頁面嵌入iframe:

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>首頁面</title>
</head>
<body>
  <h1>首頁面</h1>
  <iframe src="http://example.com/iframe.html"></iframe>
  <script>
    // 監聽消息事件,接收來自iframe的消息
    window.addEventListener('message', function(event) {
      console.log(event.data);
    }, false);
  </script>
</body>
</html>
           

在跨域頁面發送消息:

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>跨域頁面</title>
  <script>
    // 發送消息給首頁面
    window.parent.postMessage('Hello, parent window!', '*');
  </script>
</head>
<body>
  <h1>跨域頁面</h1>
</body>
</html>
           

在這個示例中,我們在首頁面中嵌入了一個跨域的iframe,并監聽message事件來接收來自iframe的消息。在跨域的iframe中,通過window.parent.postMessage()方法發送消息給首頁面,'*'表示不限制發送消息的域名,接收哪個視窗也是如此。

使用iframe嵌套實作跨域通信時,需要注意的是,需要確定首頁面和iframe頁面的信任關系,避免資訊洩露和CSRF攻擊。

8. Document.domain:在同一個父域名下的不同子域名之間,通過設定document.domain來實作跨域通路。

在同一個父域名下的不同子域名之間進行跨域操作時,由于浏覽器的同源政策,會限制JS代碼的通路權限,為了解決這個問題,可以在父域名相同的情況下,通過設定document.domain來實作跨域通路。

具體實作方式是,在不同的子域名中,将document.domain設定為相同的父域名,如将子域名A.domain.com和子域名B.domain.com中的document.domain都設定為domain.com。這樣,這兩個子域名就可以通過JS代碼來共享cookie、iframe等資源了。

需要注意的是,document.domain隻能設定為包含它的域,例如,它可以設定為domain.com或sub.domain.com,但不能設定為otherdomain.com。并且,不同子域名之間的跨域操作僅适用于協定、端口号相同的情況。

下面是一個簡單的JS代碼示例:

假設目前頁面的URL是https://sub1.example.com/index.html,想要在同域下的https://sub2.example.com/other.html頁面中,通路目前頁面的cookie。

  1. 在目前頁面(https://sub1.example.com/index.html)中,設定document.domain:
document.domain = 'example.com';
           
  1. 在同域下的另一個頁面(https://sub2.example.com/other.html)中,通過JS代碼來通路目前頁面的cookie:
// 擷取目前域下的cookie
var cookies = document.cookie;
console.log(cookies);
           

通過将兩個頁面的document.domain設定為相同的父域名,使得它們處于同一個域中,進而實作了跨域通路。

9. 跨域資源共享(CORS)标準:CORS機制通過一些特定的HTTP頭部來告訴浏覽器,哪些跨域請求是可以被允許的,進而實作跨域資料通路。

CORS(Cross-origin Resource Sharing)是一個W3C标準,用于解決通過HTTP進行跨域通路的問題。

CORS機制允許伺服器向浏覽器發送附加HTTP頭,告訴浏覽器哪些跨域請求是被允許的。

具體來說,當浏覽器向跨域伺服器請求資料時,伺服器可以在響應頭部中加入Access-Control-Allow-Origin字段。例如,如果A域名的頁面向B域名的伺服器請求資源,B伺服器可以在響應頭中添加以下字段:

Access-Control-Allow-Origin: https://A-domain.com
           

這個字段告訴浏覽器A域名下的頁面可以跨域通路B域名伺服器的資源。如果沒有設定這個字段,跨域請求将會被浏覽器阻止。

除了Access-Control-Allow-Origin字段,CORS機制還可以通過其他一些HTTP頭部實作更複雜的跨域請求。例如,Access-Control-Allow-Methods字段指定了伺服器支援的HTTP方法,Access-Control-Allow-Headers指定了伺服器接受的請求頭,Access-Control-Allow-Credentials指定了請求是否需要使用憑證等等。

下面是一個簡單的CORS示例代碼,在伺服器端需要添加Access-Control-Allow-Origin頭部。

// 定義CORS允許通路的域名
const ALLOW_ORIGIN = 'https://example.com'

const http = require('http');

http.createServer(function(req, res) {
    console.log('request method:', req.method);
    console.log('request url:', req.url);
    console.log('request headers:', req.headers);
    if (req.method === 'OPTIONS') {
        // 處理預檢請求
        res.writeHead(200, {
            'Access-Control-Allow-Origin': ALLOW_ORIGIN,//此處指定了允許通路的域名
            'Access-Control-Allow-Methods': 'POST,GET,OPTIONS',
            'Access-Control-Allow-Headers': 'X-Requested-With,Content-Type',
            'Access-Control-Allow-Credentials': 'true',
            'Content-Length': 0
        });
        res.end();
    } else {
        // 處理CORS請求
        res.writeHead(200, {
            'Access-Control-Allow-Origin': ALLOW_ORIGIN,//此處指定了允許通路的域名
            'Access-Control-Allow-Credentials': 'true',
            'Content-Type': 'application/json'
        });
        res.write(JSON.stringify({'msg': 'Hello CORS!'}));
        res.end();
    }
}).listen(3000, function() {
    console.log('Listening on port 3000');
});
           

這段代碼建立了一個HTTP伺服器,當浏覽器向該伺服器發送CORS請求時,伺服器會在響應頭中添加Access-Control-Allow-Origin和Access-Control-Allow-Credentials字段,進而允許跨域通路。

繼續閱讀