跨域:原理、机制与解决方案
一、什么是跨域?
跨域(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 | sequenceDiagram |
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 | app.use((req, res, next) => { |
2. JSONP(JSON with Padding)
原理:利用<script>标签不受同源策略限制的特性
1 | // 客户端 |
局限性:
- 仅支持GET请求
- 缺乏错误处理机制
- 存在XSS风险
3. 反向代理
原理:同源请求代理服务器 → 代理转发到目标服务器
Nginx配置示例:
1 | server { |
4. WebSocket
1 | // 客户端 |
5. postMessage
跨窗口通信:
1 | // 发送方(https://web.com) |
6. 现代浏览器API
跨域资源共享代理:
1 | // 使用CORS代理服务 |
浏览器支持:
1 | pie |
五、OPTIONS预检请求详解
1. 什么情况下触发OPTIONS请求?
- 使用非简单请求方法(PUT/DELETE等)
- 包含非标准请求头(自定义头)
- Content-Type为application/json
- 请求中带身份凭证(credentials)
2. OPTIONS请求示例
1 | OPTIONS /api/user |
3. 服务端响应示例
1 | 204 No Content |
4. 优化建议
- 使用
Access-Control-Max-Age减少预检请求 - 合并多个自定义头减少预检次数
- 尽可能使用简单请求
六、简单请求 vs 复杂请求
1. 简单请求条件
- 方法限制:
- GET
- HEAD
- POST
- 头限制:
- Accept
- Accept-Language
- Content-Language
- Content-Type(仅限于以下值):
text/plainmultipart/form-dataapplication/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 | <form action="https://other.com/api" method="POST"> |
关键特性:
- ✅ 允许跨域提交:浏览器不会阻止表单的跨域提交
- 🔄 页面跳转:表单提交后浏览器会导航到目标URL
- ❌ 响应受限:提交后浏览器会离开当前页面,无法直接处理跨域响应
2. AJAX表单提交的跨域问题
1 | // 使用AJAX提交表单 |
结果:
- 需要服务端配置CORS响应头
- 否则浏览器会阻止JavaScript读取响应
3. 解决方案
- 传统表单:接受页面跳转
- AJAX提交:
- 服务端配置CORS
- 使用代理服务器中转
- 隐藏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
2Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Embedder-Policy: require-corp
SharedArrayBuffer:
- 需要跨域隔离环境
- 启用高性能并行计算
联邦学习(Federated Learning):
- 跨域数据协作新范式
- 隐私保护下的模型训练
Web容器技术:
- 微前端架构
- 模块联邦(Module Federation)
总结
跨域是现代Web开发中的核心问题,理解其原理和解决方案至关重要:
- 同源策略是浏览器安全基石,限制跨域脚本交互
- CORS是主流解决方案,需前后端协作实现
- 简单请求直接发送,复杂请求需要预检
- 表单提交允许跨域但导致页面跳转
- OPTIONS预检是复杂请求的必要步骤
- 多种替代方案适用于不同场景(JSONP/代理/WebSocket等)
随着Web生态发展,跨域技术也在不断演进。开发人员应掌握核心原理,根据实际需求选择适当方案,同时关注新兴的跨域安全模型和技术趋势。