跨域指的是在Web浏覽器中,一個站點的腳本試圖向另一個站點的腳本發起HTTP請求,這種請求是由于浏覽器遵守同源政策(Same-Origin Policy)而被拒絕的。為了解決這個問題,我們可以使用以下9種方式:
1. JSONP:通過動态建立<script>标簽向其他域請求資料,并将資料作為回調函數參數傳遞回來。
當需要在一個網頁中通路另一個域名下的資料時,由于浏覽器的同源政策會阻止這種通路,是以就需要使用JSONP來實作跨域資料的通路。
JSONP(JSON with Padding)是一種實作跨域網絡資料傳輸的技術,它利用了<script>标簽的跨域通路特性。
使用JSONP的基本過程如下:
- 在用戶端代碼中定義一個全局的回調函數,用于接收伺服器傳回的資料。
- 建構一個動态建立
示例代碼如下:
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的基本過程如下:
- 在伺服器端設定響應頭資訊,允許指定的跨域請求通路資源。通過設定Access-Control-Allow-Origin頭資訊,指定允許通路的源,比如設定為"*"表示允許任意源通路。
- 浏覽器在發送跨域請求時,會在請求頭中添加Origin頭資訊,這個頭資訊訓示了請求的源位址。
- 伺服器在接收到請求後,會根據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的基本過程如下:
- 用戶端通過WebSocket對象建立WebSocket連接配接,指定WebSocket服務端的位址。
- WebSocket服務端接收到連接配接請求後,進行握手處理,并建立起連接配接。在握手時,服務端會告知用戶端是否支援WebSocket協定。
- 如果握手成功,用戶端和服務端就可以進行雙向資料傳輸,用戶端和服務端均可發送和接收資料。
示例代碼如下:
// 建立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的基本過程如下:
- 在發送消息的視窗中,使用postMessage方法向指定的視窗發送消息,指定視窗的origin屬性(協定、域名、端口号)以便接收視窗驗證消息是否來自信任的源。
- 在接收消息的視窗中,通過添加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. 代理伺服器:通過在伺服器端代理請求,将跨域請求轉到同一伺服器,再傳回到請求端。
代理伺服器是指在伺服器端轉發請求和響應的中間層伺服器,它可以代理用戶端向目标伺服器發送請求,擷取響應,進而實作跨域請求。
使用代理伺服器實作跨域請求的基本過程如下:
- 用戶端向代理伺服器發送請求。
- 代理伺服器接收到請求後,将請求轉發給目标伺服器,擷取響應。
- 代理伺服器将響應傳回給用戶端,完成跨域請求。
示例代碼如下:
// 在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反向代理實作跨域請求的基本步驟如下:
- 安裝nginx,并編輯配置檔案。
- 在nginx配置檔案中,使用location指令比對需要代理的URL,并設定代理伺服器的位址和端口号。
- 重新開機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嵌套實作跨域通信的基本過程如下:
- 在首頁面中定義一個iframe元素,并設定src屬性為跨域頁面的URL。
- 在跨域頁面中,通過window.parent來擷取首頁面的window對象,并向首頁面發送消息。
- 在首頁面中,通過監聽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。
- 在目前頁面(https://sub1.example.com/index.html)中,設定document.domain:
document.domain = 'example.com';
- 在同域下的另一個頁面(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字段,進而允許跨域通路。