Java案例怎么配置跨域规则?

wen java案例 203

Java案例:如何配置跨域规则?从入门到生产级配置详解

目录导读

  1. 跨域基础:为什么会出现跨域问题?
  2. Java跨域配置的三种核心方式
  3. Spring Boot案例:CORS全局配置
  4. Spring Security整合:安全与跨域共存
  5. 手写Filter:最灵活的跨域控制
  6. 常见问题与解决(Q&A)
  7. 生产环境最佳实践

跨域基础:为什么会出现跨域问题?

Q:什么是跨域? A:跨域指浏览器从一个域名的网页去请求另一个域名的资源时,由于浏览器的同源策略限制(协议、域名、端口任一不同),请求会被阻止。

Java案例怎么配置跨域规则?

Q:跨域只影响前端吗? A:是的,跨域是浏览器的安全行为,服务端本身并不阻止请求,但浏览器会拦截响应,需要在服务端配置规则,告知浏览器允许哪些域访问。

关键概念

  • Origin(源)http://localhost:8080 就是一个完整的源。
  • 预检请求(Preflight):对于非简单请求(如自定义Header、PUT/DELETE方法),浏览器会先发一个OPTIONS请求确认服务器是否允许。

Java跨域配置的三种核心方式

Java环境(Spring Boot、Servlet、Spring Security)下的跨域配置,本质上是在HTTP响应头中加入CORS相关的字段,核心Header包括:

Header字段 作用
Access-Control-Allow-Origin 允许的源(如 或 http://example.com
Access-Control-Allow-Methods 允许的HTTP方法
Access-Control-Allow-Headers 允许的请求头
Access-Control-Max-Age 预检请求的缓存时间

*Q:为什么不能直接在所有场景用 `?** A:*无法配合withCredentials`(携带Cookie)使用;且生产环境建议指定具体域名,防止任意站点访问。


Spring Boot案例:CORS全局配置

WebMvcConfigurer 配置(推荐)

@Configuration
public class CorsConfig implements WebMvcConfigurer {
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/api/**") // 匹配所有/api路径
                .allowedOrigins("http://localhost:3000") // 允许的前端域名
                .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
                .allowedHeaders("*")
                .allowCredentials(true) // 允许携带Cookie
                .maxAge(3600); // 缓存预检请求1小时
    }
}

*Q:allowCredentials(true) 和 `allowedOrigins("")为什么不能同时使用?** A:浏览器安全策略要求,当允许携带凭证时,必须明确指定可信任的源,不能使用通配符,此时可改为allowedOrigins("http://localhost:3000")`。

@CrossOrigin 注解(局部配置)

@RestController
@CrossOrigin(origins = "http://localhost:3000", maxAge = 3600)
public class UserController {
    @GetMapping("/user/info")
    public UserInfo getInfo() { /* ... */ }
}

适用场景:仅对少数接口开放跨域。


Spring Security整合:安全与跨域共存

当项目中同时使用 Spring Security 时,跨域配置必须在 Security 过滤器链之前生效,否则,未通过认证的请求会被拦截,连OPTIONS预检请求都无法通过。

配置示例

@Configuration
@EnableWebSecurity
public class SecurityConfig {
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            .cors(cors -> cors.configurationSource(corsConfigurationSource())) // 启用CORS
            .csrf(csrf -> csrf.disable()) // 注意:跨域时通常关闭CSRF
            .authorizeHttpRequests(auth -> auth
                .requestMatchers("/api/public/**").permitAll()
                .anyRequest().authenticated()
            );
        return http.build();
    }
    @Bean
    public CorsConfigurationSource corsConfigurationSource() {
        CorsConfiguration config = new CorsConfiguration();
        config.setAllowedOrigins(Arrays.asList("http://localhost:3000"));
        config.setAllowedMethods(Arrays.asList("GET","POST","PUT","DELETE","OPTIONS"));
        config.setAllowedHeaders(Arrays.asList("*"));
        config.setAllowCredentials(true);
        config.setMaxAge(3600L);
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", config);
        return source;
    }
}

Q:为什么跨域时需要关闭CSRF? A:跨域请求本身可能携带Cookie,而CSRF防护会检查Token,实际项目中,如果前端与后端不在同一域名,可采用Token认证(如JWT)替代Cookie认证。


手写Filter:最灵活的跨域控制

对于非Spring Boot项目或需自定义逻辑的场景,可使用Filter拦截所有请求,动态添加Header。

@WebFilter("/*")
public class SimpleCorsFilter 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://localhost:3000");
        response.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
        response.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization");
        response.setHeader("Access-Control-Max-Age", "3600");
        // 预检请求直接返回200
        if ("OPTIONS".equalsIgnoreCase(((HttpServletRequest) req).getMethod())) {
            response.setStatus(HttpServletResponse.SC_OK);
        } else {
            chain.doFilter(req, res);
        }
    }
}

Q:为什么要单独处理OPTIONS请求? A:预检请求不需要真正的业务处理,直接返回成功状态即可,这样可以减轻后续过滤器(如认证过滤器)的负担。


常见问题与解决(Q&A)

Q:配置后前端依然报跨域错误?

  • 检查是否配置了Access-Control-Allow-Credentials但未指定具体域名。
  • 确认请求头中Origin值是否在allowedOrigins列表里。
  • 检查是否有Nginx等代理层也拦截了跨域响应头。

Q:生产环境如何动态配置允许的域名? 使用配置项:@Value("${cors.allowed.origins}") 从application.yml读取域名列表,而非硬编码。

Q:局部@CrossOrigin与全局WebMvcConfigurer冲突吗? 注解优先级更高,若全局配置允许所有源,但某个接口用注解限制了源,则注解生效。


生产环境最佳实践

  1. *杜绝使用 `**:生产环境务必指定具体的源列表,如http://www.example.com`。
  2. 单独管理跨域配置中心:可用数据库或配置中心动态更新允许的源,避免重启服务。
  3. 预检请求缓存优化:设置合理的 maxAge(如86400秒即24小时),减少频繁的OPTIONS请求。
  4. 结合API网关:在网关层统一配置跨域,后端应用无需再关心跨域逻辑。
  5. 安全建议:跨域与CSRF、XSS防护结合使用,对携带Cookie的请求,确保Https传输。

跨域配置的核心在于服务端正确返回CORS响应头,根据项目架构选择配置方式——简单单体用WebMvcConfigurer,有Security用SecurityFilterChain,非Spring项目用Filter,生产环境记住两条铁律:明确源 + 关闭通配符


本文基于Spring Boot 2.7+和Spring Security 6.x编写,适用主流Java Web应用场景。

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