一、跨域核心概念:同源策略與跨域定義
跨域問題的根源是瀏覽器的?同源策略(Same-Origin Policy),這是瀏覽器為保護用戶數據安全而設置的核心安全限制。
1. 什么是 “同源”?
“同源” 指的是兩個 URL 的?協議、域名、端口號?三者完全一致。只有滿足同源條件,瀏覽器才允許 JS 腳本相互訪問數據(如 AJAX 請求、操作 DOM 等)。
URL 示例 | 與?http://www.example.com:8080/index.html ?是否同源 | 原因分析 |
---|---|---|
http://www.example.com:8080/about.html | 是 | 協議(http)、域名、端口完全一致 |
https://www.example.com:8080/index.html | 否 | 協議不同(http vs https) |
http://blog.example.com:8080/index.html | 否 | 域名不同(www vs blog,二級域名差異) |
http://www.example.com:80/index.html | 否 | 端口不同(8080 vs 80) |
http://www.example.org:8080/index.html | 否 | 主域名不同(example.com?vs?example.org) |
2. 什么是 “跨域”?
當 JS 腳本嘗試訪問?非同源?的資源(如發起 AJAX 請求、獲取非同源頁面的 DOM)時,就會觸發瀏覽器的同源策略限制,這種場景稱為 “跨域”。
常見跨域場景:
- 前端項目部署在?
http://localhost:5500
,請求后端接口?http://localhost:3000
(端口不同)。 - 本地開發時,請求線上接口(如前端?
http://127.0.0.1
?請求?https://api.taobao.com
,域名不同)。
3. 同源策略的作用
同源策略并非 “限制”,而是 “保護”:
- 防止惡意網站通過 JS 讀取用戶在其他網站的 Cookie(如登錄狀態)。
- 防止惡意網站篡改非同源頁面的 DOM,偽造用戶操作。
- 避免敏感數據(如用戶信息、支付數據)被未授權的腳本竊取。
二、跨域解決方案:前端與后端配合實現
解決跨域的核心思路是 “繞過或允許” 同源策略限制,常見方案分為?前端主導(如 JSONP)、后端主導(如 CORS)和?代理轉發(如 Nginx 代理)三類。以下重點講解前端常用的 JSONP 和后端配置的 CORS。
1. 方案一:JSONP(前端主導,僅支持 GET 請求)
JSONP(JSON with Padding)是早期解決跨域的經典方案,利用?<script>
?標簽不受同源策略限制的特性實現跨域請求。
1.1 JSONP 原理
<script>
?標簽的?src
?屬性可以加載任意域名的資源(如 CDN 上的 JS 文件),瀏覽器不攔截。- 后端返回的不是純 JSON 數據,而是?“回調函數名 ( JSON 數據)”?的 JS 代碼。
- 前端提前定義好回調函數,當?
<script>
?加載并執行后端返回的 JS 代碼時,會自動調用回調函數,從而獲取數據。
1.2 JSONP 實現步驟(前端 + 后端)
(1)前端實現(以 “淘寶商品搜索建議” 為例)
html
預覽
<!-- 輸入框:用戶輸入關鍵詞 -->
<input type="text" id="searchInput" placeholder="輸入商品關鍵詞">
<button id="searchBtn">搜索</button>
<ul id="suggestList"></ul><script>
// 1. 提前定義回調函數:接收并處理后端返回的數據
function handleSuggest(data) {const suggestList = document.getElementById("suggestList");suggestList.innerHTML = ""; // 清空舊數據// 渲染搜索建議(淘寶接口返回的 data.result 是二維數組)data.result.forEach(item => {const li = document.createElement("li");li.innerText = item[0]; // item[0] 是商品名稱suggestList.appendChild(li);});
}// 2. 點擊按鈕發起 JSONP 請求
document.getElementById("searchBtn").onclick = function() {const keyword = document.getElementById("searchInput").value;if (!keyword) return;// 3. 創建 <script> 標簽,通過 src 發起跨域請求const script = document.createElement("script");// 淘寶開放接口:cb 參數指定回調函數名(必須與前端定義的一致)script.src = `http://suggest.taobao.com/sug?code=utf-8&q=${encodeURIComponent(keyword)}&callback=handleSuggest`;// 4. 將 <script> 插入頁面,觸發請求document.body.appendChild(script);// 5. 請求完成后移除 <script> 標簽(避免頁面冗余)script.onload = function() {document.body.removeChild(script);};
};
</script>
(2)后端實現(PHP 示例)
若后端是自建接口(如 PHP),需配合返回 “回調函數包裹的 JSON”:
php
<?php
// 1. 獲取前端傳遞的回調函數名(參數名通常為 callback)
$callback = $_GET['callback'];// 2. 準備要返回的數據(模擬商品搜索建議)
$data = ["code" => 200,"msg" => "success","result" => [["iPhone 15", "10000+ 銷量"],["iPhone 15 Pro", "5000+ 銷量"],["iPhone 15 殼", "20000+ 銷量"]]
];// 3. 轉換為 JSON 字符串
$jsonData = json_encode($data);// 4. 輸出:回調函數名( JSON 數據 )
echo $callback . "(" . $jsonData . ")";
// 最終輸出:handleSuggest({"code":200,"msg":"success",...})
?>
1.3 JSONP 的優缺點
優點 | 缺點 |
---|---|
兼容性好(支持所有瀏覽器,包括 IE) | 僅支持 GET 請求(無法發送 POST/PUT/DELETE) |
無需后端復雜配置(僅需返回特定格式) | 安全性低(可能遭受 XSS 攻擊,需信任數據源) |
前端實現簡單 | 無法捕獲請求錯誤(如 404/500,<script> ?加載失敗無回調) |
2. 方案二:CORS(后端主導,支持所有 HTTP 方法)
CORS(Cross-Origin Resource Sharing,跨域資源共享)是 W3C 標準,也是目前解決跨域的?主流方案。它通過后端在響應頭中添加特定字段,明確告知瀏覽器 “允許該域名跨域訪問”,從而繞過同源策略限制。
2.1 CORS 原理
- 前端發起跨域請求時,瀏覽器會先發送一個?預檢請求(OPTIONS 請求)(復雜請求,如 POST/PUT),詢問后端 “是否允許當前域名訪問”。
- 后端通過響應頭(如?
Access-Control-Allow-Origin
)告知瀏覽器允許的域名、方法、頭部信息。 - 瀏覽器驗證響應頭后,若符合條件,則允許前端接收數據;否則攔截請求。
2.2 CORS 實現步驟(后端配置 + 前端 AJAX)
(1)后端配置(以 PHP 為例)
只需在后端接口中添加 CORS 響應頭即可,核心字段如下:
php
<?php
// 1. 允許的前端域名(* 表示允許所有域名,生產環境建議指定具體域名)
header("Access-Control-Allow-Origin: http://localhost:5500");// 2. 允許的請求方法(GET/POST/PUT/DELETE 等)
header("Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS");// 3. 允許的請求頭(如 Content-Type、Authorization)
header("Access-Control-Allow-Headers: Content-Type");// 4. 允許前端攜帶 Cookie(需配合 withCredentials,生產環境慎用)
// header("Access-Control-Allow-Credentials: true");// 5. 處理預檢請求(OPTIONS 請求):直接返回 204 狀態碼
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {http_response_code(204);exit;
}// 6. 業務邏輯:返回數據
$response = ["code" => 200,"msg" => "CORS 跨域請求成功","data" => ["username" => "張三", "age" => 20]
];
echo json_encode($response);
?>
(2)前端 AJAX 請求(與普通 AJAX 無差異)
html
預覽
<button id="corsBtn">發起 CORS 請求</button>
<div id="corsResult"></div><script>
document.getElementById("corsBtn").onclick = function() {const xhr = new XMLHttpRequest();// 跨域請求:前端 http://localhost:5500 請求后端 http://localhost:3000xhr.open("POST", "http://localhost:3000/api/user", true);// 設置請求頭(需與后端 Access-Control-Allow-Headers 匹配)xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");// 發送請求(POST 參數)xhr.send("username=張三");// 處理響應xhr.onreadystatechange = function() {if (xhr.readyState === 4 && xhr.status === 200) {const result = JSON.parse(xhr.responseText);document.getElementById("corsResult").innerText = JSON.stringify(result, null, 2);}};
};
</script>
2.3 CORS 的優缺點
優點 | 缺點 |
---|---|
支持所有 HTTP 方法(GET/POST/PUT/DELETE) | 后端需額外配置(但配置簡單,一次配置全局生效) |
安全性高(可精確控制允許的域名、方法) | 部分舊瀏覽器不支持(如 IE 8/9,需兼容時可用 JSONP) |
支持攜帶 Cookie(需配置 withCredentials) | 復雜請求會多一次預檢請求(OPTIONS),輕微影響性能 |
3. 其他跨域方案(補充)
除了 JSONP 和 CORS,實際開發中還可能用到以下方案:
- Nginx 反向代理:前端請求本地 Nginx 服務器,Nginx 將請求轉發到后端(因 Nginx 是服務器端,不受同源策略限制)。
- PostMessage:用于兩個非同源頁面之間的通信(如 iframe 父子頁面),通過?
window.postMessage()
?發送數據,window.addEventListener("message")
?接收數據。 - WebSocket:WebSocket 是全雙工通信協議,一旦建立連接,不受同源策略限制(適合實時通信場景,如聊天、直播)。