跨域问题详解
总体来说两点:
- cors
- nginx 配置域名映射
什么是跨域问题
跨域问题是浏览器的安全机制(同源策略),当网页从一个域名向另一个域名发送请求时,浏览器会阻止这种请求。
同源策略判断标准
浏览器检查三个要素:协议 + 域名 + 端口
javascript
// 当前页面:https://admin.company.com:443
// ✅ 同源请求
'https://admin.company.com:443/api/data'
'https://admin.company.com/other-page'
// ❌ 跨域请求
'http://admin.company.com/api' // 协议不同
'https://api.company.com/data' // 域名不同
'https://admin.company.com:8080/api' // 端口不同
为什么要限制跨域
1. 保护用户隐私
javascript
// 恶意网站 evil.com 的页面
// 如果没有跨域限制,可以:
fetch('https://bank.com/account/balance') // 窃取银行信息
fetch('https://social.com/api/messages') // 窃取私人消息
2. 防止 CSRF 攻击
javascript
// 利用用户登录状态执行恶意操作
fetch('https://bank.com/transfer', {
method: 'POST',
credentials: 'include',
body: JSON.stringify({
to: 'hacker-account',
amount: 10000
})
});
3. 防止信息泄露
javascript
// 探测内网信息
fetch('http://192.168.1.100:8080/admin/users')
.then(() => console.log('发现内网服务!'))
跨域问题的表现
浏览器报错信息
csharp
Access to fetch at 'http://api.example.com/data'
from origin 'https://web.example.com'
has been blocked by CORS policy:
No 'Access-Control-Allow-Origin' header is present on the requested resource.
前端代码失败
javascript
fetch('http://api.example.com/data')
.then(response => response.json())
.catch(error => {
console.error('跨域请求被阻止!', error);
// 功能无法正常使用
});
重要概念澄清
跨域只存在于浏览器环境
javascript
// ❌ 有跨域问题:浏览器中的网页请求
fetch('https://api.other-domain.com/data')
// ✅ 没有跨域问题:服务器端调用
// Java后端调用其他服务
restTemplate.getForObject("https://api.other-domain.com/data", String.class);
跨域是前端问题,不是后端问题
- 后端服务之间的调用不受跨域限制
- 只有浏览器中的JavaScript请求才会被跨域策略阻止
跨域解决方案
1. CORS 配置(最常用)
后端设置响应头
java
// Java Spring 示例
@CrossOrigin(origins = "https://web.company.com")
@RestController
public class ApiController {
@GetMapping("/data")
public ResponseEntity<String> getData() {
return ResponseEntity.ok("success");
}
}
全局 CORS 配置
java
@Configuration
public class CorsConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("https://web.company.com")
.allowedMethods("GET", "POST", "PUT", "DELETE")
.allowedHeaders("*")
.allowCredentials(true)
.maxAge(3600);
}
}
2. 域名映射/反向代理(推荐)
Nginx 配置
nginx
server {
listen 80;
server_name web.company.com;
# 前端页面
location / {
proxy_pass http://frontend-server;
}
# API请求代理到后端
location /api/ {
proxy_pass http://backend-server/;
proxy_set_header Host $proxy_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
效果
javascript
// 前端代码改为同域请求
fetch('/api/data') // 浏览器认为是同域,不会跨域
3. 前端代理(开发环境)
Webpack 配置
javascript
// webpack.config.js
module.exports = {
devServer: {
proxy: {
'/api': {
target: 'http://backend-server.com',
changeOrigin: true,
pathRewrite: {
'^/api': ''
}
}
}
}
};
4. 后端代理
java
// 在前端同域的后端添加代理接口
@RestController
public class ProxyController {
@Autowired
private RestTemplate restTemplate;
@GetMapping("/api/proxy/data")
public ResponseEntity<String> proxyData() {
// 后端调用其他服务,没有跨域问题
String result = restTemplate.getForObject(
"http://other-service.com/data",
String.class
);
return ResponseEntity.ok(result);
}
}
实际应用建议
生产环境推荐做法
- 统一域名:通过反向代理让前后端在同一域名下
- CORS配置:如果必须跨域,在后端配置允许的源
开发环境常见做法
arduino
前端开发服务器:http://localhost:3000
后端开发服务器:http://localhost:8080
通过前端代理解决跨域
企业级部署
bash
用户访问:https://app.company.com
页面请求:https://app.company.com/dashboard → 前端
API请求: https://app.company.com/api/users → 后端
# 通过 Nginx 反向代理实现统一域名
总结
- 🔒 跨域是浏览器安全机制,保护用户免受恶意攻击
- 🌐 只影响浏览器中的请求,服务器间调用不受限制
- 🛠️ 主流解决方案:域名映射/反向代理 + CORS配置
- ✅ 最佳实践:前后端保持同域名,通过代理实现
- 🚫 避免:完全关闭跨域限制,会带来安全风险
跨域问题虽然常见,但有成熟的解决方案。选择合适的方案可以在保证安全的前提下,提供良好的用户体验。