文章中英模式
布魯斯前端面試題目 - 跨域是什麼?怎麼解決跨域問題?
深入解析跨來源資源共用(CORS)的工作原理、常見錯誤與解決方案。掌握前端面試中關於跨域的核心知識,包含各種解決跨域問題的方法。
文章中英模式
懶得看文章?那就來看影片吧
什麼是跨域(CORS)?
跨來源資源共用(Cross-Origin Resource Sharing, CORS)是一種瀏覽器安全機制,用於控制不同來源(網域、協議或端口)之間的資源請求。簡單來說,它就像是網站間的「國境管制」,決定哪些外部資源可以被存取。
舉個例子:假設你正在瀏覽 https://shopping.com 的網站,而這個網站需要從 https://api.shopping.com 獲取商品資料。雖然它們看起來很相似,但因為域名不同,瀏覽器會視為「跨域請求」,預設會阻止這種存取。
這就像你在一家商店(前端網站)想要取得另一家商店(API服務器)的商品,但需要對方明確同意才行。如果API服務器沒有說「我允許shopping.com存取我的資源」,瀏覽器就會擋下這個請求。
⚠️ 同源的定義:
兩個URL必須具有相同的協議(http/https)、域名和端口才被視為同源。就像住在同一棟大樓的鄰居。
- 1. https://example.com 和 https://api.example.com 不同源(子域名不同,像是同一條街不同棟大樓)
- 2. http://example.com 和 https://example.com 不同源(協議不同,一個用走路一個用開車到同一地點)
- 3. https://example.com 和 https://example.com:8080 不同源(端口不同,像是同一棟大樓不同樓層)
CORS的工作機制
CORS有兩種主要請求類型:簡單請求和預檢請求。
類型 | 特點 | 流程 | 使用場景 |
---|---|---|---|
簡單請求 | • GET/HEAD/POST方法 • 基本標頭 • 簡單Content-Type | 直接發送請求,帶Origin標頭 | 簡單表單提交、基礎GET請求 |
預檢請求 (主流用法) | • 特殊HTTP方法 • 自定義標頭 • JSON等特殊Content-Type | 1. 先發OPTIONS請求詢問 2. 服務器回應許可 3. 再發實際請求 | 現代API請求、JSON數據交換、帶認證的請求 |
在現代Web開發中,預檢請求是主流情況,因為大多數API請求都使用JSON格式和自定義標頭,這些都會觸發預檢機制。
// 預檢請求
OPTIONS /api/data HTTP/1.1
Host: api.example.com
Origin: https://example.com
Access-Control-Request-Method: POST // 即使是簡單方法(POST)
Access-Control-Request-Headers: Content-Type, X-Custom-Header // 但有自定義標頭觸發預檢
// 預檢響應
HTTP/1.1 204 No Content
Access-Control-Allow-Origin: https://example.com
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers: Content-Type, X-Custom-Header
Access-Control-Max-Age: 86400 // 預檢結果緩存時間(秒)
常見CORS錯誤
Access to fetch at 'https://api.example.com' from origin 'https://example.com' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
最常見的CORS錯誤,表示服務器未設置Access-Control-Allow-Origin標頭。
Access to fetch at 'https://api.example.com' from origin 'https://example.com' has been blocked by CORS policy: Method PUT is not allowed by Access-Control-Allow-Methods in preflight response.
服務器未在Access-Control-Allow-Methods中允許請求的HTTP方法。
Access to fetch at 'https://api.example.com' from origin 'https://example.com' has been blocked by CORS policy: Request header field X-Custom-Header is not allowed by Access-Control-Allow-Headers in preflight response.
服務器未在Access-Control-Allow-Headers中允許請求的自定義標頭。
解決跨域問題的方法
1. 後端配置CORS標頭
最標準的解決方案是在後端伺服器正確配置CORS標頭:
// Node.js Express示例
const express = require('express');
const app = express();
// 配置CORS中間件
app.use((req, res, next) => {
res.header('Access-Control-Allow-Origin', 'https://example.com'); // 或使用 * 允許所有來源
res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization');
res.header('Access-Control-Allow-Credentials', 'true'); // 允許攜帶憑證
// 處理預檢請求
if (req.method === 'OPTIONS') {
return res.status(204).end();
}
next();
});
// 或使用cors套件
const cors = require('cors');
app.use(cors({
origin: 'https://example.com',
methods: ['GET', 'POST', 'PUT', 'DELETE'],
allowedHeaders: ['Content-Type', 'Authorization'],
credentials: true
}));
2. 使用代理伺服器(Proxy)
在前端開發環境中,可以配置代理伺服器轉發請求:
// webpack開發服務器代理配置
module.exports = {
// ...
devServer: {
proxy: {
'/api': {
target: 'https://api.example.com',
changeOrigin: true,
pathRewrite: { '^/api': '' }
}
}
}
}
// Next.js代理配置
// next.config.js
module.exports = {
async rewrites() {
return [
{
source: '/api/:path*',
destination: 'https://api.example.com/:path*',
},
]
},
}
3. 使用後端中繼伺服器
在Next.js 15中使用Route Handlers作為中繼伺服器轉發請求:
這種方法有效是因為同源策略只適用於瀏覽器到伺服器的請求,而伺服器之間的請求不受CORS限制。
當我們的Next.js應用作為中繼伺服器時,瀏覽器只與同源的Next.js伺服器通信,然後由Next.js伺服器轉發請求到第三方API並將響應返回給前端,從而繞過了CORS限制。
// app/api/proxy/route.ts
import { NextRequest, NextResponse } from 'next/server';
export async function GET(request: NextRequest) {
const searchParams = request.nextUrl.searchParams;
try {
// 從第三方API獲取數據
const response = await fetch(
`https://api.example.com/data?${searchParams}`
);
const data = await response.json();
// 返回數據給前端
return NextResponse.json(data);
} catch (error) {
return NextResponse.json(
{ error: 'Failed to fetch data' },
{ status: 500 }
);
}
}
4. 其他解決方案
- JSONP (僅適用於GET請求):利用script標籤不受同源策略限制的特性,但只支援GET請求且安全性較低。
- 瀏覽器擴展:開發時可使用CORS瀏覽器擴展暫時禁用CORS,但僅適用於開發環境。
- 反向代理:使用Nginx、Apache等反向代理伺服器轉發請求。
CORS最佳實踐
1. 安全性優先
- 避免使用 * 允許所有來源,明確指定允許的域名
- 只允許必要的HTTP方法和標頭
- 謹慎使用Access-Control-Allow-Credentials
2. 效能優化
- 設置適當的Access-Control-Max-Age緩存預檢結果
- 避免不必要的預檢請求,使用簡單請求格式
3. 開發便利性
- 開發環境可使用代理或瀏覽器擴展
- 生產環境必須正確配置CORS標頭
🔥 常見面試題目
(一) 什麼是CORS? 為什麼瀏覽器需要同源政策?
解答:CORS是瀏覽器的安全機制,允許服務器聲明哪些網站可以訪問它的資源。同源政策就像網站間的防火牆,限制不同網站間的資料存取。
沒有同源政策的危險:
惡意網站(evil.com) 用戶的銀行網站(bank.com)
| |
| 用戶訪問惡意網站 |
| 同時已登入銀行網站 |
| |
|-- 嘗試讀取銀行資料 --X |
| |
| 瀏覽器阻止請求 |
| (同源政策保護) |
如果沒有同源政策,當你登入銀行網站後,再訪問惡意網站時,惡意網站可能會偷偷讀取你的銀行資料或執行轉帳等操作。CORS則是在需要跨域訪問時,提供安全的標準機制。
(二) 解決CORS問題的最佳方法有哪些? 各有什麼優缺點?
解答:解決CORS的主要方法:
方法 | 優點 | 缺點 |
---|---|---|
後端配置CORS標頭 | 標準方案,配置靈活 | 需要後端配合 |
代理伺服器 | 前端可獨立解決 | 增加請求複雜度 |
後端中繼伺服器 | 完全控制請求處理 | 需維護額外服務 |
最理想的解決方案是後端正確配置CORS標頭。如果無法修改API源,則考慮使用代理或中繼伺服器。
(三) 在前後端分離的開發環境中如何處理CORS問題?
解答:前後端分離開發環境中的CORS解決方案:
1. 開發環境代理:
// Vite (vite.config.js/ts)
export default defineConfig({
server: {
proxy: {
'/api': {
target: 'http://localhost:8080',
changeOrigin: true,
rewrite: (path) => path.replace(/^/api/, '')
}
}
}
});
2. 後端開發環境配置:在開發環境允許localhost域名訪問
3. 中間代理伺服器:如果前端有使用Next.js,可以考慮使用Next.js的代理功能。
// app/api/proxy/route.ts
import { NextRequest, NextResponse } from 'next/server';
export async function GET(request: NextRequest) {
const searchParams = request.nextUrl.searchParams;
try {
// 從第三方API獲取數據
const response = await fetch(
`https://api.example.com/data?${searchParams}`
);
const data = await response.json();
// 返回數據給前端
return NextResponse.json(data);
} catch (error) {
return NextResponse.json(
{ error: 'Failed to fetch data' },
{ status: 500 }
);
}
}