1. 跨域问题的本质与产生原因
跨域问题本质上是一个浏览器安全策略,专业术语叫"同源策略"(Same-Origin Policy)。这个策略规定:当协议、域名、端口三者中任意一个不同时,浏览器就会认为这是跨域请求,默认会阻止这种请求。
举个例子:
- 前端运行在
https://www.example.com - 后端API在
https://api.example.com虽然域名相似,但子域名不同,浏览器就会拦截这个请求。
注意:跨域限制是浏览器行为,不是服务器限制。用Postman等工具直接请求API是能正常响应的。
2. 后端解决方案详解
2.1 CORS标准解决方案
CORS(Cross-Origin Resource Sharing)是W3C标准,也是最推荐的跨域解决方案。后端需要设置以下响应头:
Access-Control-Allow-Origin: * Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS Access-Control-Allow-Headers: Content-Type, Authorization Access-Control-Allow-Credentials: true各语言实现示例:
Node.js (Express)
app.use((req, res, next) => { res.header("Access-Control-Allow-Origin", "*"); res.header("Access-Control-Allow-Headers", "Content-Type"); next(); });Java Spring Boot
@Configuration public class CorsConfig implements WebMvcConfigurer { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") .allowedOrigins("*") .allowedMethods("*"); } }PHP
header("Access-Control-Allow-Origin: *"); header("Access-Control-Allow-Methods: GET, POST, PUT, DELETE");2.2 生产环境的安全考虑
虽然示例中使用*允许所有域名,但在生产环境应该:
- 明确指定允许的域名
- 根据请求来源动态设置
Access-Control-Allow-Origin - 对于需要携带凭证(cookie)的请求,不能使用
*
3. 前端解决方案与注意事项
3.1 开发环境代理方案
在开发阶段,可以通过webpack-dev-server或vite配置代理:
webpack.config.js
devServer: { proxy: { '/api': { target: 'http://backend-server', changeOrigin: true, pathRewrite: { '^/api': '' } } } }vite.config.js
server: { proxy: { '/api': { target: 'http://backend-server', changeOrigin: true, rewrite: path => path.replace(/^\/api/, '') } } }3.2 特殊场景处理
文件下载的跨域处理
// 创建隐藏的iframe const iframe = document.createElement('iframe'); iframe.style.display = 'none'; iframe.src = downloadUrl; document.body.appendChild(iframe); // 5秒后移除 setTimeout(() => { document.body.removeChild(iframe); }, 5000);WebSocket跨域WebSocket不受同源策略限制,但后端仍需配置:
const ws = new WebSocket('wss://api.example.com');4. 其他跨域解决方案对比
4.1 JSONP方案(已过时)
function handleResponse(data) { console.log(data); } const script = document.createElement('script'); script.src = 'https://api.example.com/data?callback=handleResponse'; document.body.appendChild(script);缺点:
- 仅支持GET请求
- 安全性较差
- 逐渐被CORS取代
4.2 Nginx反向代理
location /api/ { proxy_pass http://backend-server/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; }优势:
- 前端无需任何修改
- 后端无需处理CORS
- 适合生产环境部署
5. 常见问题排查指南
5.1 预检请求(OPTIONS)问题
复杂请求(如Content-Type为application/json)会先发OPTIONS请求。后端必须正确处理:
// Express中间件示例 app.options('*', (req, res) => { res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE'); res.header('Access-Control-Allow-Headers', 'Content-Type'); res.sendStatus(200); });5.2 凭证(Cookie)携带问题
需要同时满足:
- 前端设置
withCredentials: true - 后端设置
Access-Control-Allow-Credentials: true - 不能使用
Access-Control-Allow-Origin: *,必须指定具体域名
5.3 缓存问题
浏览器可能会缓存CORS响应头,导致修改后不生效。解决方案:
- 开发时禁用缓存
- 生产环境给URL添加版本号或随机参数
6. 安全最佳实践
- 不要盲目开放所有跨域:精确控制允许的源、方法和头信息
- 敏感接口额外保护:即使开放跨域,也要有身份验证
- 定期审计CORS配置:检查是否有过度开放的配置
- 考虑使用CSRF Token:防止跨站请求伪造
实际项目中,我通常会创建一个专门的CORS配置模块,根据环境变量动态设置允许的域名列表,这样既方便开发调试,又能保证生产环境安全。