一、什么是跨域?
跨域(Cross-Origin)是Web开发中的一个核心安全概念,指的是当浏览器中的一个网页脚本尝试访问与当前页面不同源的资源时,浏览器会施加的安全限制。这种限制是由浏览器的**同源策略(Same-Origin Policy)**强制执行的安全机制。
1.1 同源策略详解
同源策略是浏览器最基础的安全机制之一,它要求两个URL必须同时满足以下三个条件才能被认为是同源的:
-
协议相同(Protocol)
- 例如都是
http://
或都是https://
http://example.com
与https://example.com
❌ 不同源
- 例如都是
-
域名相同(Host)
- 包括主域名和子域名
http://a.example.com
与http://b.example.com
❌ 不同源
-
端口相同(Port)
- 默认端口(http为80,https为443)需要显式对比
http://example.com:80
与http://example.com:8080
❌ 不同源
1.2 典型跨域场景示例
当前页面URL | 请求目标URL | 是否跨域 | 原因 |
---|---|---|---|
http://a.com/index.html | http://a.com/api | 同源 | - |
https://a.com/index.html | http://a.com/api | 跨域 | 协议不同 |
http://a.com:80/index.html | http://a.com:8080/api | 跨域 | 端口不同 |
http://a.com/index.html | http://b.com/api | 跨域 | 域名不同 |
http://sub.a.com/index.html | http://a.com/api | 跨域 | 子域名与主域名不同 |
1.3 为什么需要同源策略?
同源策略的存在是为了解决关键的安全问题:
-
防止CSRF攻击
- 恶意网站不能直接使用你的登录态访问其他网站
-
保护DOM安全
- 阻止恶意iframe获取敏感页面内容
-
隔离存储数据
- Cookie、LocalStorage等数据按源隔离
二、跨域解决方案全景图
2.1 CORS(跨域资源共享)——现代标准方案
实现原理:
服务端配置(Node.js示例):
const express = require('express');
const cors = require('cors');
const app = express();
// 基础CORS配置
app.use(cors({
origin: ['https://yourdomain.com', 'https://yourapp.com'],
methods: ['GET', 'POST', 'PUT', 'DELETE'],
allowedHeaders: ['Content-Type', 'Authorization'],
exposedHeaders: ['X-Total-Count'],
credentials: true,
maxAge: 86400
}));
// 动态源配置(高级用法)
app.use(cors({
origin: function(origin, callback) {
const allowedOrigins = [/\.yourdomain\.com$/, 'https://partner.com'];
const isValid = !origin || allowedOrigins.some(pattern =>
typeof pattern === 'string'
? origin === pattern
: pattern.test(origin)
);
callback(isValid ? null : new Error('Not allowed'), isValid);
}
}));
2.2 JSONP(传统方案)
工作原理:
安全风险警示:
// 恶意服务器可能返回
callback({
data: "看似正常的数据",
maliciousCode: function() {
// 执行恶意操作
}
});
2.3 代理服务器方案对比
方案类型 | 实现方式 | 适用场景 | 优点 | 缺点 |
---|---|---|---|---|
开发环境代理 | webpack-dev-server proxy | 本地开发 | 零配置 | 仅限开发环境 |
Nginx反向代理 | location /api { proxy_pass } | 生产环境 | 高性能、负载均衡 | 需要运维知识 |
Node中间件代理 | http-proxy-middleware | 全栈应用 | 灵活配置 | 增加服务器负载 |
三、深度技术解析
3.1 CORS预检请求的底层细节
当请求满足以下任一条件时,浏览器会发送预检请求(OPTIONS):
- 使用
PUT
、DELETE
等非简单方法 - 包含自定义头部(如
X-Auth-Token
) Content-Type
不是以下三种之一:text/plain
multipart/form-data
application/x-www-form-urlencoded
网络抓包示例:
OPTIONS /resource HTTP/1.1
Host: api.example.com
Origin: https://yourdomain.com
Access-Control-Request-Method: DELETE
Access-Control-Request-Headers: X-Custom-Header
3.2 跨域Cookie的完整流程
-
前端设置:
fetch('https://api.com/login', { credentials: 'include', // 必须显式开启 headers: { 'Content-Type': 'application/json' } });
-
服务端要求:
HTTP/1.1 200 OK Access-Control-Allow-Origin: https://yourdomain.com Access-Control-Allow-Credentials: true Set-Cookie: sessionId=abc123; SameSite=None; Secure; Domain=.api.com
-
关键限制:
- 不能使用
Access-Control-Allow-Origin: *
- Cookie必须设置
SameSite=None; Secure
- 不能使用
四、企业级最佳实践
4.1 安全加固方案
生产环境推荐配置:
const helmet = require('helmet');
const rateLimit = require('express-rate-limit');
app.use(helmet());
app.use(rateLimit({
windowMs: 15 * 60 * 1000,
max: 100,
message: 'Too many requests from this IP'
}));
app.use(cors({
origin: [/\.yourcompany\.com$/],
methods: ['GET', 'POST'],
allowedHeaders: ['Content-Type'],
credentials: true
}));
4.2 监控与审计
-
日志记录:
app.use((req, res, next) => { console.log(`[${new Date().toISOString()}] ${req.method} ${req.path} Origin: ${req.headers.origin}`); next(); });
-
报警机制:
- 监控异常的
Origin
头部 - 统计被拒绝的预检请求
- 监控异常的
五、特殊场景处理
5.1 WebSocket跨域
const WebSocket = require('ws');
const wss = new WebSocket.Server({
verifyClient: (info, cb) => {
const isValid = info.origin === 'https://yourdomain.com';
cb(isValid, isValid ? 200 : 401);
}
});
5.2 跨域图片资源
<img src="https://cross-origin.com/image.jpg" crossorigin="anonymous">
<script>
const img = new Image();
img.crossOrigin = 'Anonymous';
img.onload = function() {
// 安全使用图片数据
};
img.src = 'https://cross-origin.com/image.jpg';
</script>
六、演进与未来
-
CORP(Cross-Origin Resource Policy):
Cross-Origin-Resource-Policy: same-site
-
COOP(Cross-Origin Opener Policy):
Cross-Origin-Opener-Policy: same-origin
-
新标准趋势:
- 更细粒度的跨域控制
- 默认安全性增强
- 更严格的第三方资源限制
通过本文的系统性讲解,开发者可以深入理解跨域问题的本质,掌握各种解决方案的适用场景,并能在实际项目中做出合理的技术决策。记住:良好的跨域策略应该像精心设计的门禁系统,既要保证合法访问的畅通,又要确保非法请求的拦截。