跨域:原理、机制与解决方案 一、什么是跨域? 跨域(Cross-Origin) 是指浏览器出于安全考虑,限制网页脚本向不同源(协议+域名+端口) 的服务发起请求的行为。这是浏览器实现的同源策略(Same-Origin Policy) 所导致的安全限制。
同源策略三要素
协议相同 (HTTP/HTTPS)
域名相同 (www.example.com)
端口相同 (80/443)
示例 :
当前页面URL
请求URL
是否同源
原因
https://www.example.com
https://www.example.com/api
✅
协议、域名、端口相同
http://www.example.com
https://www.example.com
❌
协议不同(HTTP vs HTTPS)
https://example.com
https://api.example.com
❌
域名不同(主域 vs 子域)
https://www.example.com:8080
https://www.example.com
❌
端口不同(8080 vs 443)
二、跨域场景分析 1. 常见跨域场景
前端与API分离 :https://web.com 请求 https://api.com
微服务架构 :https://service1.com 请求 https://service2.com
CDN资源 :https://main.com 加载 https://cdn.com/resource.js
第三方服务 :https://myapp.com 接入 https://maps.google.com
2. 跨域限制范围
操作类型
是否允许跨域
示例
链接跳转
✅
<a href="https://other.com">
资源嵌入
✅
<img src="https://other.com/img.png">
表单提交
✅
<form action="https://other.com">
AJAX请求
❌
fetch('https://other.com/api')
Cookie访问
❌
document.cookie 读取其他域名cookie
DOM操作
❌
iframe.contentDocument 跨域访问
三、浏览器跨域判断机制 1. CORS(跨域资源共享)流程 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 sequenceDiagram participant Browser participant Server Browser->>Server: 发送跨域请求(带Origin头) alt 简单请求 Server-->>Browser: 响应包含Access-Control-Allow-Origin Browser->>Browser: 检查响应头是否匹配Origin else 复杂请求 Browser->>Server: 先发送OPTIONS预检请求 Server-->>Browser: 返回CORS策略头 Browser->>Browser: 验证策略是否允许 Browser->>Server: 发送实际请求 Server-->>Browser: 返回实际响应 end
2. 核心响应头字段
响应头字段
作用
示例
Access-Control-Allow-Origin
允许的源
https://web.com 或 *
Access-Control-Allow-Methods
允许的HTTP方法
GET, POST, PUT
Access-Control-Allow-Headers
允许的请求头
Content-Type, Authorization
Access-Control-Allow-Credentials
是否允许发送Cookie
true
Access-Control-Max-Age
预检请求缓存时间
86400(1天)
四、跨域解决方案详解 1. CORS(跨域资源共享) 服务端配置示例(Node.js) :
1 2 3 4 5 6 7 8 9 10 11 12 13 app.use ((req, res, next ) => { res.setHeader ('Access-Control-Allow-Origin' , 'https://web.com' ); res.setHeader ('Access-Control-Allow-Methods' , 'GET, POST, OPTIONS' ); res.setHeader ('Access-Control-Allow-Headers' , 'Content-Type, Authorization' ); res.setHeader ('Access-Control-Allow-Credentials' , 'true' ); res.setHeader ('Access-Control-Max-Age' , '86400' ); if (req.method === 'OPTIONS' ) { return res.sendStatus (200 ); } next (); });
2. JSONP(JSON with Padding) 原理 :利用<script>标签不受同源策略限制的特性
1 2 3 4 5 6 7 8 9 10 11 function handleResponse (data ) { console .log ('Received:' , data); } const script = document .createElement ('script' );script.src = 'https://api.com/data?callback=handleResponse' ; document .body .appendChild (script);handleResponse ({"name" : "John" , "age" : 30 });
局限性 :
仅支持GET请求
缺乏错误处理机制
存在XSS风险
3. 反向代理 原理 :同源请求代理服务器 → 代理转发到目标服务器
Nginx配置示例 :
1 2 3 4 5 6 7 8 9 10 server { listen 80 ; server_name web.com; location /api/ { proxy_pass https://api.com/; proxy_set_header Host $host ; proxy_set_header X-Real-IP $remote_addr ; } }
4. WebSocket 1 2 3 4 5 6 7 8 9 10 const socket = new WebSocket ('wss://api.com/ws' );socket.onopen = () => { socket.send (JSON .stringify ({action : 'subscribe' })); }; socket.onmessage = (event ) => { console .log ('Data:' , JSON .parse (event.data )); };
5. postMessage 跨窗口通信 :
1 2 3 4 5 6 7 8 9 const iframe = document .getElementById ('other-site' );iframe.contentWindow .postMessage ('Hello!' , 'https://other.com' ); window .addEventListener ('message' , (event ) => { if (event.origin !== 'https://web.com' ) return ; console .log ('Received:' , event.data ); });
6. 现代浏览器API 跨域资源共享代理 :
1 2 3 fetch ('https://cors-proxy.com/https://api.com/data' ) .then (response => response.json ())
浏览器支持 :
1 2 3 4 5 6 pie title 跨域方案浏览器支持率 "CORS" : 98 "postMessage" : 97 "WebSocket" : 95 "JSONP" : 99
五、OPTIONS预检请求详解 1. 什么情况下触发OPTIONS请求?
使用非简单请求方法(PUT/DELETE等)
包含非标准请求头(自定义头)
Content-Type为application/json
请求中带身份凭证(credentials)
2. OPTIONS请求示例 1 2 3 4 5 OPTIONS /api/user HTTP/1.1 Host : api.comOrigin : https://web.comAccess-Control-Request-Method : DELETEAccess-Control-Request-Headers : X-Custom-Header
3. 服务端响应示例 1 2 3 4 5 6 HTTP/1.1 204 No ContentAccess-Control-Allow-Origin : https://web.comAccess-Control-Allow-Methods : GET, POST, DELETEAccess-Control-Allow-Headers : X-Custom-HeaderAccess-Control-Max-Age : 86400Access-Control-Allow-Credentials : true
4. 优化建议
使用Access-Control-Max-Age减少预检请求
合并多个自定义头减少预检次数
尽可能使用简单请求
六、简单请求 vs 复杂请求 1. 简单请求条件
方法限制 :
头限制 :
Accept
Accept-Language
Content-Language
Content-Type(仅限于以下值):
text/plain
multipart/form-data
application/x-www-form-urlencoded
简单请求流程 :
浏览器直接发送跨域请求(带Origin头)
服务器响应包含CORS头
浏览器验证响应头
2. 复杂请求条件 任何不符合简单请求条件的请求,包括:
PUT、DELETE、PATCH等方法
Content-Type为application/json
包含自定义头(如Authorization)
复杂请求流程 :
浏览器发送OPTIONS预检请求
服务器响应预检请求
浏览器验证通过后发送实际请求
服务器响应实际请求
3. 对比总结
特性
简单请求
复杂请求
请求次数
1次
2次(预检+实际)
触发条件
符合特定方法/头限制
超出简单请求范围
性能影响
小
较大(多一次往返)
常见场景
表单提交、普通AJAX
API请求、带认证请求
示例
fetch('https://api.com', { method: 'POST' })
fetch('https://api.com', { method: 'PUT', headers: {'Authorization': 'Bearer ...'} })
七、表单提交与跨域 1. 表单提交的特性 1 2 3 4 <form action ="https://other.com/api" method ="POST" > <input type ="text" name ="username" > <button type ="submit" > 提交</button > </form >
关键特性 :
✅ 允许跨域提交 :浏览器不会阻止表单的跨域提交
🔄 页面跳转 :表单提交后浏览器会导航到目标URL
❌ 响应受限 :提交后浏览器会离开当前页面,无法直接处理跨域响应
2. AJAX表单提交的跨域问题 1 2 3 4 5 6 7 8 9 10 const formData = new FormData (document .getElementById ('myForm' ));fetch ('https://other.com/api' , { method : 'POST' , body : formData }).then (response => { console .log (response); });
结果 :
需要服务端配置CORS响应头
否则浏览器会阻止JavaScript读取响应
3. 解决方案
传统表单 :接受页面跳转
AJAX提交 :
隐藏iframe技巧 :1 2 3 4 <form target ="hiddenFrame" action ="https://other.com/api" method ="POST" > </form > <iframe name ="hiddenFrame" style ="display:none" > </iframe >
八、安全最佳实践
精确配置CORS :
1 2 res.setHeader ('Access-Control-Allow-Origin' , 'https://trusted-domain.com' );
凭证控制 :
1 2 3 4 5 fetch (url, { credentials : 'include' });res.setHeader ('Access-Control-Allow-Credentials' , 'true' );
CSRF防护 :
使用SameSite Cookie属性
实现CSRF令牌机制
验证Origin/Referer头
避免JSONP安全风险 :
严格验证回调函数名
实施内容安全策略(CSP)
优先使用CORS替代JSONP
九、现代跨域技术趋势
跨域隔离(Cross-Origin Isolation) :
使用COOP(Cross-Origin Opener Policy)
使用COEP(Cross-Origin Embedder Policy)1 2 Cross-Origin-Opener-Policy : same-originCross-Origin-Embedder-Policy : require-corp
SharedArrayBuffer :
联邦学习(Federated Learning) :
Web容器技术 :
微前端架构
模块联邦(Module Federation)
总结 跨域是现代Web开发中的核心问题,理解其原理和解决方案至关重要:
同源策略 是浏览器安全基石,限制跨域脚本交互
CORS 是主流解决方案,需前后端协作实现
简单请求 直接发送,复杂请求 需要预检
表单提交 允许跨域但导致页面跳转
OPTIONS预检 是复杂请求的必要步骤
多种替代方案适用于不同场景(JSONP/代理/WebSocket等)
随着Web生态发展,跨域技术也在不断演进。开发人员应掌握核心原理,根据实际需求选择适当方案,同时关注新兴的跨域安全模型和技术趋势。