Java实现跨域处理案例

wen java案例 2

Java后端跨域处理全解析:从CORS原理到Spring Boot实战案例


目录导读

  1. 跨域问题的本质:浏览器的同源策略
  2. CORS机制核心概念:预检请求与响应头
  3. Java生态主流跨域处理方案对比
  4. Spring Boot 3.x 跨域配置实战(含Filter与注解)
  5. 复杂场景问答:携带Cookie、自定义Header、放行精确域名
  6. 常见坑点排查:为什么配置了还是报跨域错误?

跨域问题的本质:浏览器的同源策略

浏览器出于安全考虑,会阻止协议、域名、端口任一不同的资源请求,这就是“同源策略”,例如前端站点http://a.com:8080访问后端接口http://localhost:8081/api,即构成跨域,需要明确:跨域限制是浏览器的行为,并非服务端拒绝请求,实际请求已经到达后端,只是响应被浏览器拦截。

Java实现跨域处理案例

理解这一点至关重要,因为这意味着后端必须显式告知浏览器“我允许该源访问”,从而解除拦截。


CORS机制核心概念:预检请求与响应头

跨域资源共享(CORS)是W3C标准,通过在后端响应中添加特定HTTP头来声明允许的跨域策略。

  • 简单请求(如GET/POST,且Content-Type为application/x-www-form-urlencoded等):直接发送,但响应须包含Access-Control-Allow-Origin
  • 预检请求(Preflight):当请求包含自定义Header、或使用PUT/DELETE等非简单方法时,浏览器会先发送一个OPTIONS请求,询问服务器允许的规则,服务器必须正确响应OPTIONS,并返回关键的三个头:
    • Access-Control-Allow-Origin(必填,指定允许的源,或)
    • Access-Control-Allow-Methods(允许的方法,如GET, POST, PUT, DELETE
    • Access-Control-Allow-Headers(允许的请求头,如Content-Type, Authorization

Java生态主流跨域处理方案对比

方案 适用场景 优点 缺点
Spring @CrossOrigin 注解 单个Controller或方法级 快速、直观 配置分散,不利于全局管理
全局CorsFilter/WebMvcConfigurer 整个应用统一策略 集中管理,灵活度高 需注意过滤器执行顺序
Nginx反向代理 生产环境前后端分离 前端无感,性能好 需额外运维配置,与Java代码解耦
JSONP 仅限GET请求老系统 简单 有安全漏洞,已不推荐

推荐优先使用Spring的WebMvcConfigurer全局配置,配合@CrossOrigin做局部细粒度覆盖。


Spring Boot 3.x 跨域配置实战(含Filter与注解)

案例背景:前端http://admin.example.com:3000需访问后端http://api.internal.cn:8081

方案A:全局CorsConfigurer(推荐)

@Configuration
public class CorsConfig implements WebMvcConfigurer {
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**") // 拦截所有路径
            .allowedOrigins("http://admin.example.com:3000") // 精确放行,勿用*
            .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
            .allowedHeaders("*")
            .exposedHeaders("Authorization") // 前端需要读取的响应头
            .allowCredentials(true) // 允许携带Cookie或Authorization
            .maxAge(3600); // 预检请求缓存1小时,减少OPTIONS请求
    }
}

方案B:基于Filter泛化处理(适合非Spring MVC或自研框架)

@Component
public class SimpleCorsFilter implements Filter {
    @Override
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) {
        HttpServletResponse response = (HttpServletResponse) res;
        response.setHeader("Access-Control-Allow-Origin", "http://admin.example.com:3000");
        response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
        response.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization");
        response.setHeader("Access-Control-Allow-Credentials", "true");
        if ("OPTIONS".equalsIgnoreCase(((HttpServletRequest) req).getMethod())) {
            return; // 直接拦截OPTIONS,不进入业务逻辑
        }
        chain.doFilter(req, res);
    }
}

方案C:局部注解(用于某个特殊接口)

@RestController
@CrossOrigin(origins = "https://special.vip.com", methods = {RequestMethod.GET})
public class SpecialController { ... }

注意:若同时配置了全局与注解,注解会覆盖全局配置,应避免对同一路径重复定义。


复杂场景问答:携带Cookie、自定义Header、放行精确域名

*问:配置了`allowedOrigins("")`,但前端需求要携带Cookie,为什么浏览器还是报错?**

答:CORS规范规定,当allowCredentials=true时,Access-Control-Allow-Origin不能为,必须指定明确的源地址,这是安全策略,防止恶意站点利用Cookie伪造身份,必须改为明确的前端源地址,如http://admin.example.com:3000

问:前端请求中带了X-Requested-With: XMLHttpRequest或自定义Token头,后端仍需处理吗?

答:是的,如果未在allowedHeaders中显式声明该Header,预检请求会失败,应配置allowedHeaders("*")或指定具体头名称,特别注意,如需暴露某些响应头给前端JS读取(如Authorization),需加上.exposedHeaders("Authorization")

问:为什么我的PUT请求总是发送两次(一次OPTIONS,一次PUT)?

答:这是浏览器预检机制的正常表现,第一次OPTIONS用于“试探”,确定服务器允许跨域后,浏览器才发送真实PUT请求,可通过.maxAge(3600)缓存预检结果,减少OPTIONS请求次数。


常见坑点排查:为什么配置了还是报跨域错误?

  1. 过滤器优先级:如果你自定义了登录鉴权Filter,且该Filter先于CorsFilter执行,当返回401/403错误时,响应头中缺少CORS信息,浏览器同样拦截,解决办法:确保CorsFilter的执行顺序在最前(如@Order(Ordered.HIGHEST_PRECEDENCE))。

  2. 网关/反向代理丢失响应头:若经过Nginx或Spring Cloud Gateway转发,需确认代理层没有丢弃或覆盖Access-Control-Allow-*头。

  3. 简单请求不触发预检但仍被拦截:检查Access-Control-Allow-Origin返回值是否与前端Origin完全一致,不能有多余空格。

  4. 错误使用HttpServletResponse直接写值:在拦截器中直接对响应对象setHeader,但未通过CorsRegistry配置,后续请求可能被Spring MVC覆盖。

抱歉,评论功能暂时关闭!