Spring Boot处理跨域请求(CORS)的完整指南

跨域资源共享(CORS)是现代Web开发中常见的问题,Spring Boot提供了多种方式来处理CORS请求。下面我将详细介绍各种实现方式及其适用场景。

一、CORS基础概念

1. 什么是跨域请求?

当浏览器从一个域名的网页去请求另一个域名的资源时,如果域名、端口或协议不同,就会产生跨域请求。出于安全考虑,浏览器默认会阻止这类请求。

2. 简单请求 vs 预检请求

类型条件处理方式
简单请求GET/HEAD/POST方法,且Content-Type为text/plain、multipart/form-data或application/x-www-form-urlencoded直接发送请求,带Origin头
预检请求(OPTIONS)不符合简单请求条件的其他请求先发送OPTIONS请求,获得许可后再发送实际请求

二、Spring Boot处理CORS的5种方式

1. 使用@CrossOrigin注解

适用场景:针对单个控制器或方法级别的CORS配置

@RestController
@RequestMapping("/api")
public class MyController {
    
    // 允许特定源的跨域访问
    @CrossOrigin(origins = "https://example.com")
    @GetMapping("/resource")
    public ResponseEntity<String> getResource() {
        return ResponseEntity.ok("跨域资源");
    }
    
    // 更详细的配置
    @CrossOrigin(origins = {"https://example.com", "https://api.example.com"},
                allowedHeaders = {"Content-Type", "Authorization"},
                methods = {RequestMethod.GET, RequestMethod.POST},
                maxAge = 3600)
    @PostMapping("/save")
    public ResponseEntity<String> saveResource() {
        return ResponseEntity.ok("保存成功");
    }
}

2. 全局CORS配置

适用场景:应用级别的统一CORS配置

@Configuration
public class WebConfig implements WebMvcConfigurer {
    
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/api/**")  // 匹配的路径
                .allowedOrigins("https://example.com", "https://api.example.com") // 允许的源
                .allowedMethods("GET", "POST", "PUT", "DELETE") // 允许的方法
                .allowedHeaders("*") // 允许的请求头
                .exposedHeaders("Authorization", "Content-Disposition") // 暴露的响应头
                .allowCredentials(true) // 是否允许发送cookie
                .maxAge(3600); // 预检请求缓存时间(秒)
        
        // 可以添加多个配置
        registry.addMapping("/public/**")
                .allowedOrigins("*");
    }
}

3. 使用Filter处理CORS

适用场景:需要更底层控制或与非Spring Web环境集成

@Configuration
public class CorsFilterConfig {
    
    @Bean
    public FilterRegistrationBean<CorsFilter> corsFilter() {
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        CorsConfiguration config = new CorsConfiguration();
        
        // 配置CORS规则
        config.setAllowCredentials(true);
        config.addAllowedOrigin("https://example.com");
        config.addAllowedHeader("*");
        config.addAllowedMethod("*");
        config.setMaxAge(3600L);
        
        // 对所有路径生效
        source.registerCorsConfiguration("/**", config);
        
        FilterRegistrationBean<CorsFilter> bean = 
            new FilterRegistrationBean<>(new CorsFilter(source));
        bean.setOrder(Ordered.HIGHEST_PRECEDENCE); // 设置最高优先级
        
        return bean;
    }
}

4. Spring Security中的CORS配置

适用场景:使用Spring Security的项目

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.cors().and() // 启用CORS支持
            .csrf().disable() // 通常CORS和CSRF不能同时使用
            .authorizeRequests()
            .antMatchers("/api/public/**").permitAll()
            .anyRequest().authenticated();
    }
    
    // 提供CORS配置源
    @Bean
    CorsConfigurationSource corsConfigurationSource() {
        CorsConfiguration configuration = new CorsConfiguration();
        configuration.setAllowedOrigins(Arrays.asList("https://example.com"));
        configuration.setAllowedMethods(Arrays.asList("GET", "POST"));
        configuration.setAllowedHeaders(Arrays.asList("*"));
        
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", configuration);
        
        return source;
    }
}

5. 响应头手动设置

适用场景:需要动态控制CORS头

@RestController
public class DynamicCorsController {
    
    @GetMapping("/dynamic-cors")
    public ResponseEntity<String> dynamicCors(HttpServletRequest request, 
                                           HttpServletResponse response) {
        // 根据请求动态设置CORS头
        String origin = request.getHeader("Origin");
        if (isAllowedOrigin(origin)) {
            response.setHeader("Access-Control-Allow-Origin", origin);
            response.setHeader("Access-Control-Allow-Credentials", "true");
            response.setHeader("Access-Control-Allow-Methods", "GET, POST");
        }
        
        return ResponseEntity.ok("动态CORS响应");
    }
    
    private boolean isAllowedOrigin(String origin) {
        // 实现你的源验证逻辑
        return origin != null && origin.endsWith("example.com");
    }
}

三、CORS配置详解

1. 核心响应头说明

响应头说明
Access-Control-Allow-Origin允许访问的源,可以是具体域名或*(不推荐使用*,特别是需要凭证时)
Access-Control-Allow-Methods允许的HTTP方法(GET, POST等)
Access-Control-Allow-Headers允许的请求头
Access-Control-Expose-Headers浏览器可以访问的响应头
Access-Control-Allow-Credentials是否允许发送cookie(true/false),设为true时Allow-Origin不能为*
Access-Control-Max-Age预检请求结果的缓存时间(秒)

2. 常见问题解决方案

问题1:预检请求(OPTIONS)被拦截

解决方案

  • 确保OPTIONS请求不被安全框架拦截
  • 在Spring Security中配置:
    http.cors().and()
        .authorizeRequests()
        .antMatchers(HttpMethod.OPTIONS, "/**").permitAll()
    
问题2:带凭证的请求失败

解决方案

  • 确保allowCredentials(true)和具体的allowedOrigins(不能是*)
  • 前端需要设置withCredentials: true(如axios)
问题3:特定响应头无法获取

解决方案

  • 使用exposedHeaders暴露需要的头:
    .exposedHeaders("Custom-Header", "Authorization")
    

四、最佳实践建议

  1. 生产环境不要使用通配符*:明确指定允许的源
  2. 合理限制HTTP方法:只开放必要的方法(GET/POST等)
  3. 考虑使用环境变量:动态配置允许的源
    @Value("${cors.allowed.origins}")
    private String[] allowedOrigins;
    
    // 在配置中使用
    .allowedOrigins(allowedOrigins)
    
  4. 结合安全框架:Spring Security项目使用专门的CORS配置
  5. 测试不同场景:简单请求和预检请求都要测试

五、完整配置示例

@Configuration
@EnableWebMvc
public class CorsConfig implements WebMvcConfigurer {
    
    @Value("${app.cors.allowed-origins}")
    private String[] allowedOrigins;
    
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/api/**")
                .allowedOrigins(allowedOrigins)
                .allowedMethods("GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS")
                .allowedHeaders("*")
                .exposedHeaders("Authorization", "Content-Disposition")
                .allowCredentials(true)
                .maxAge(3600);
                
        registry.addMapping("/public/**")
                .allowedOrigins("*")
                .allowedMethods("GET", "OPTIONS");
    }
    
    // 可选:提供CORS过滤器作为备选
    @Bean
    public FilterRegistrationBean<CorsFilter> corsFilter() {
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        CorsConfiguration config = new CorsConfiguration();
        config.applyPermitDefaultValues();
        config.setAllowCredentials(true);
        config.setAllowedOrigins(Arrays.asList(allowedOrigins));
        source.registerCorsConfiguration("/**", config);
        
        FilterRegistrationBean<CorsFilter> bean = new FilterRegistrationBean<>(new CorsFilter(source));
        bean.setOrder(Ordered.HIGHEST_PRECEDENCE);
        return bean;
    }
}

六、总结

Spring Boot提供了多种灵活的方式来处理CORS:

  1. 简单场景:使用@CrossOrigin注解
  2. 统一配置:实现WebMvcConfigureraddCorsMappings方法
  3. 底层控制:配置CorsFilter
  4. 安全项目:结合Spring Security的cors()配置
  5. 动态需求:手动设置响应头

根据项目需求选择合适的方式,并遵循安全最佳实践,可以有效地解决跨域问题,同时保证应用的安全性。
在这里插入图片描述

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

北辰alk

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值