后端接口的 “跨域问题” 全解析:从 “报错拦截” 到 “安全共享”

155 阅读4分钟

在前后端分离架构中,“跨域” 是开发者绕不开的问题 —— 前端页面在http://localhost:3000请求后端http://localhost:8080的接口时,浏览器会因 “同源策略” 拦截响应,报出Access to fetch at '...' from origin '...' has been blocked by CORS policy错误。跨域问题的本质是浏览器的安全限制,解决它需要在 “安全” 与 “便捷” 之间找到平衡,实现合法的跨域资源共享。

跨域的核心概念与判定标准

什么是同源策略?

浏览器的同源策略要求 “协议、域名、端口” 三者完全一致才视为 “同源”,否则即为跨域。例如:

  • http://example.com 与 https://example.com:协议不同 → 跨域

  • http://example.com 与 http://api.example.com:域名不同 → 跨域

  • http://example.com:80 与 http://example.com:8080:端口不同 → 跨域

同源策略的目的是防止恶意网站窃取其他网站的用户数据(如 Cookie、LocalStorage),但也阻碍了合法的前后端分离架构通信。

跨域请求的分类

  • 简单请求:满足 “GET/POST/HEAD 方法 + 仅含简单请求头(如 Content-Type 为 application/x-www-form-urlencoded)” 的请求,直接发送并根据响应头判断是否允许跨域。
  • 预检请求(OPTIONS) :复杂请求(如 PUT/DELETE 方法、含自定义头、Content-Type 为 application/json)会先发送 OPTIONS 请求,询问服务器是否允许跨域,只有通过后才发送实际请求。

后端解决跨域的三种方案

1. 全局 CORS 配置:最常用的跨域解决方案

通过 Spring Boot 的@CrossOrigin注解或全局配置类,允许指定来源的跨域请求:

// 全局CORS配置类
@Configuration
public class CorsConfig implements WebMvcConfigurer {
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**") // 对所有接口生效
                .allowedOrigins("http://localhost:3000", "https://example.com") // 允许的前端域名
                .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") // 允许的请求方法
                .allowedHeaders("*") // 允许的请求头
                .exposedHeaders("Authorization") // 允许前端获取的响应头
                .allowCredentials(true) // 允许携带Cookie
                .maxAge(3600); // 预检请求的缓存时间(秒),减少OPTIONS请求次数
    }
}

关键参数说明

  • allowedOrigins:指定允许的域名(生产环境不要用*,避免安全风险)
  • allowCredentials: true:允许跨域请求携带 Cookie(此时allowedOrigins不能为*
  • maxAge:设置预检请求的缓存时间,减少重复 OPTIONS 请求的开销

2. 网关层跨域:适合微服务架构

在 API 网关(如 Spring Cloud Gateway)统一处理跨域,避免在每个微服务中重复配置:

# Spring Cloud Gateway跨域配置
spring:
  cloud:
    gateway:
      globalcors:
        cors-configurations:
          '[/**]':
            allowed-origins:
              - "http://localhost:3000"
              - "https://example.com"
            allowed-methods: "*"
            allowed-headers: "*"
            allow-credentials: true
            max-age: 3600

优势

  • 集中管理跨域规则,无需在各服务中配置
  • 可结合网关的路由规则,为不同路径设置不同跨域策略

3. JSONP:兼容老式浏览器的方案

JSONP 利用<script>标签不受同源策略限制的特性,通过回调函数获取跨域数据(仅支持 GET 请求):

@GetMapping("/jsonp/data")
public String getJsonpData(@RequestParam String callback) {
    // 业务逻辑:获取数据
    Map<String, Object> data = new HashMap<>();
    data.put("id", 1);
    data.put("name", "示例数据");
    // 包装成JSONP格式(callback函数调用)
    return callback + "(" + JSON.toJSONString(data) + ")";
}

前端调用:

<script src="http://localhost:8080/jsonp/data?callback=handleData"></script>
<script>
  function handleData(data) {
    console.log("跨域数据:", data); // 输出后端返回的数据
  }
</script>

局限性

  • 仅支持 GET 方法,不适合复杂请求
  • 存在 XSS 安全风险(需严格校验回调函数名)
  • 现代项目已基本被 CORS 替代

跨域配置的安全风险与规避

1. 过度宽松的配置导致安全漏洞

  • 风险:allowedOrigins: "*" 允许所有域名跨域,可能被恶意网站利用窃取数据
  • 规避:仅允许信任的域名(如公司前端域名、合作方域名)

2. 携带 Cookie 的跨域风险

  • 风险:allowCredentials: true 时,若allowedOrigins*,浏览器会拒绝响应;即使配置正确,也需防范 CSRF 攻击
  • 规避:结合 CSRF Token 验证,确保请求来自合法前端

3. 预检请求过多影响性能

  • 风险:复杂请求每次都发送 OPTIONS 预检请求,增加网络开销
  • 规避:合理设置maxAge(如 3600 秒),缓存预检结果;尽量使用简单请求(如 POST+application/x-www-form-urlencoded)

避坑指南

  • 前后端分离必配跨域:开发环境先配置好跨域,避免调试时被跨域报错干扰

  • 注意 Cookie 的 SameSite 属性:若跨域请求需要携带 Cookie,需确保 Cookie 的SameSite属性为None(同时需设置Secure属性,仅在 HTTPS 下生效)

  • 排查跨域问题的步骤:

    1. 查看浏览器控制台的具体错误信息(是 Origin 不允许还是方法不允许)

    2. 检查 Response Headers 是否包含Access-Control-Allow-*相关头

    3. 复杂请求需确认 OPTIONS 预检请求是否成功(状态码 200)

跨域问题虽看似简单,却涉及浏览器安全机制的细节。合理的跨域配置既能让前后端顺畅通信,又能守住安全底线,这是后端开发者在前后端分离架构中必须掌握的 “基础功”。