跨域

跨域问题详解

总体来说两点:

  1. cors
  2. 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);
    }
}

实际应用建议

生产环境推荐做法

  1. 统一域名:通过反向代理让前后端在同一域名下
  2. 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配置
  • 最佳实践:前后端保持同域名,通过代理实现
  • 🚫 避免:完全关闭跨域限制,会带来安全风险

跨域问题虽然常见,但有成熟的解决方案。选择合适的方案可以在保证安全的前提下,提供良好的用户体验。

相关推荐
后端小张1 小时前
【JAVA进阶】Spring Boot 核心知识点之自动配置:原理与实战
java·开发语言·spring boot·后端·spring·spring cloud·自动配置
X***C8626 小时前
SpringBoot:几种常用的接口日期格式化方法
java·spring boot·后端
i***t9196 小时前
Spring Boot项目接收前端参数的11种方式
前端·spring boot·后端
o***74177 小时前
基于SpringBoot的DeepSeek-demo 深度求索-demo 支持流式输出、历史记录
spring boot·后端·lua
9***J6287 小时前
Spring Boot项目集成Redisson 原始依赖与 Spring Boot Starter 的流程
java·spring boot·后端
S***q1927 小时前
Rust在系统工具中的内存安全给代码上了三道保险锁。但正是这种“编译期的严苛”,换来了运行时的安心。比如这段代码:
开发语言·后端·rust
v***7947 小时前
Spring Boot 热部署
java·spring boot·后端
追逐时光者8 小时前
C#/.NET/.NET Core优秀项目和框架2025年11月简报
后端·.net
码事漫谈8 小时前
Reactor网络模型深度解析:从并发困境说起
后端
T***u3338 小时前
Rust在Web中的 Web框架
开发语言·后端·rust