专业编程教程与实战项目分享平台

网站首页 > 技术文章 正文

Spring Boot解决跨域最全指南:从入门到放弃?不,到根治!

ins518 2025-06-03 22:28:33 技术文章 14 ℃ 0 评论

"明明接口测试工具能调通,前端调用就报CORS错误!"
"跨域配置加了一堆注解,为什么生产环境突然失效?"
"用了Spring Security后跨域配置全崩了?"

作为全栈开发的必经之痛,跨域问题看似简单实则暗藏杀机。本文将用6种段位解决方案 + 3个真实翻车案例,带你彻底终结Spring Boot跨域难题!


一、跨域本质三句话

  1. 浏览器安全策略:同源策略(Same-Origin Policy)的主动防御
  2. 触发条件:协议/域名/端口任一不同即跨域
  3. 解决方案核心:服务端返回正确的CORS响应头

二、青铜到王者:6种解决方案段位进阶

青铜段位:@CrossOrigin注解(适合新手尝鲜)

java

@RestController
@CrossOrigin(origins = "http://localhost:8080", 
             allowedHeaders = "*",
             methods = {RequestMethod.GET, RequestMethod.POST})
public class UserController {
    // 仅对当前Controller生效
}

致命缺陷

  • 每个Controller都要重复配置
  • 不支持携带Cookie(credentials问题)
  • 无法处理OPTIONS预检请求的缓存

白银段位:全局CORS配置(推荐常规使用)

java

@Configuration
public class CorsConfig implements WebMvcConfigurer {
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
                .allowedOrigins("*") // 生产环境建议指定具体域名
                .allowedMethods("*")
                .allowedHeaders("*")
                .exposedHeaders("Authorization") // 暴露自定义Header
                .allowCredentials(true) // 允许携带Cookie
                .maxAge(3600); // 预检请求缓存时间
    }
}

避坑指南

  • allowCredentials(true)时,allowedOrigins不能为*,必须指定具体域名
  • 若同时使用Spring Security,需配合@EnableWebSecurity配置(下文详解)

黄金段位:自定义过滤器(应对特殊场景)

java

@Component
public class CorsFilter implements Filter {
    @Override
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) 
        throws IOException, ServletException {
        HttpServletResponse response = (HttpServletResponse) res;
        response.setHeader("Access-Control-Allow-Origin", "http://your-frontend.com");
        response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
        response.setHeader("Access-Control-Max-Age", "3600");
        response.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization");
        response.setHeader("Access-Control-Allow-Credentials", "true");
        
        if ("OPTIONS".equalsIgnoreCase(((HttpServletRequest) req).getMethod())) {
            response.setStatus(HttpServletResponse.SC_OK);
        } else {
            chain.doFilter(req, res);
        }
    }
}

适用场景

  • 需要动态控制跨域策略(如多租户系统)
  • 处理特殊Header要求(如自定义签名头)

铂金段位:整合Spring Security(高危操作区)

java

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.cors() // 启用Spring Security的CORS支持
            .and()
            // 必须禁用CSRF否则会与CORS冲突
            .csrf().disable() 
            .authorizeRequests()
            .anyRequest().authenticated();
    }

    @Bean
    CorsConfigurationSource corsConfigurationSource() {
        CorsConfiguration configuration = new CorsConfiguration();
        configuration.setAllowedOrigins(Arrays.asList("http://trusted-domain.com"));
        configuration.setAllowedMethods(Arrays.asList("*"));
        configuration.setAllowedHeaders(Arrays.asList("*"));
        configuration.setAllowCredentials(true);
        
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", configuration);
        return source;
    }
}

血泪教训

  • 同时存在WebMvcConfigurer和Security配置时,以Security配置为准
  • 必须显式调用.cors()并配置CorsConfigurationSource
  • 使用OAuth2等鉴权方案时,要注意Filter顺序

钻石段位:网关层统一处理(微服务架构推荐)

yaml

# 以Spring Cloud Gateway为例
spring:
  cloud:
    gateway:
      globalcors:
        cors-configurations:
          '[/**]':
            allowed-origins: "https://*.your-domain.com"
            allowed-methods: "*"
            allowed-headers: "*"
            allow-credentials: true
            max-age: 1800

架构优势

  • 统一管控所有微服务的跨域策略
  • 避免每个服务重复配置
  • 支持动态路由更新

王者段位:Nginx反向代理(终极解决方案)

nginx

server {
    listen 80;
    server_name api.your-domain.com;
    
    location / {
        proxy_pass http://spring-boot-app:8080;
        
        # 关键CORS配置
        add_header 'Access-Control-Allow-Origin' $http_origin always;
        add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS' always;
        add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization' always;
        add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range' always;
        add_header 'Access-Control-Allow-Credentials' 'true' always;
        
        if ($request_method = 'OPTIONS') {
            return 204;
        }
    }
}

降维打击优势

  • 完全解耦业务代码
  • 支持复杂跨域策略(如多域名动态匹配)
  • 性能损耗最低

三、三大经典翻车现场复盘

翻车1:配置了allowCredentials却报错"Credentials not supported"

原因

  • 响应头Access-Control-Allow-Origin设置为*
  • 浏览器安全策略禁止credentials与通配符共存

解决方案

  • 明确指定allowedOrigins为具体域名列表
  • 前端axios等库需要设置withCredentials: true

翻车2:POST请求变成OPTIONS请求后404

原因

  • 未正确处理预检请求(OPTIONS方法)
  • Spring Security等组件拦截了OPTIONS请求

解决方案

  • 确保服务端处理OPTIONS请求(全局配置或过滤器)
  • 在Security配置中放行OPTIONS请求:
  • java
  • 复制
  • 下载
  • .antMatchers(HttpMethod.OPTIONS, "/**").permitAll()

翻车3:本地开发正常,上线后跨域失效

根本原因

  • 生产环境Nginx未正确传递Origin头
  • 网关层缓存了旧的CORS配置

排查步骤

  1. 检查浏览器Network面板实际响应头
  2. 使用curl命令测试:

bash


curl -H "Origin: http://your-domain.com" -I https://api.your-domain.com
  1. 确认Nginx配置中的add_header指令位置正确

四、最佳实践清单

  1. 开发环境:使用全局CORS配置+allowedOrigins("*")快速验证
  2. 生产环境
  3. 网关/Nginx层精确控制允许的域名
  4. 禁用allowedOrigins("*")和allowedHeaders("*")
  5. 安全加固
  6. 限制exposedHeaders仅暴露必要Header
  7. 对Access-Control-Max-Age设置合理缓存时间
  8. 鉴权体系
  9. 携带Cookie时确保SameSite属性配置
  10. JWT等方案要处理Authorization头暴露

五、终极验证大法

在Chrome开发者工具中检查响应头是否包含:

http

Access-Control-Allow-Origin: [正确域名]
Access-Control-Allow-Credentials: true  # 如需携带凭证
Vary: Origin  # 防止CDN缓存错误策略

结语

跨域不是bug,而是浏览器守护你的安全防线。点赞收藏本文,下次遇到CORS问题时,对照这个“生存手册”逐级排查,从此告别熬夜抓狂!

讨论区开放:你遇过最奇葩的跨域问题是什么? 欢迎留言,点


本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表