# SpringBoot 2.X如何全方位解决CORS跨域 ## 一、CORS跨域问题概述 ### 1.1 什么是跨域问题 跨域问题(Cross-Origin Resource Sharing,CORS)是浏览器基于**同源策略**(Same-Origin Policy)实施的安全限制。当Web应用尝试从一个源(协议+域名+端口)请求另一个源的资源时,浏览器会阻止这种请求。 同源策略要求以下三者必须完全相同: - 协议(http/https) - 域名(example.com/sub.example.com属于不同域) - 端口(默认80/443) ### 1.2 为什么需要解决跨域 现代Web应用常见架构: - 前后端分离部署(前端域名:`app.com`,后端:`api.com`) - 微服务间调用 - 第三方API集成 ### 1.3 CORS的工作原理 浏览器在发送实际请求前会先发送**预检请求**(Preflight Request,OPTIONS方法),服务器需要响应正确的CORS头部才能通过验证。 ## 二、SpringBoot 2.X的解决方案全景图 ### 2.1 解决方案分类 | 方案类型 | 适用场景 | 实现复杂度 | |-----------------------|-------------------------|-----------| | `@CrossOrigin`注解 | 单个控制器方法级别 | ★☆☆☆☆ | | WebMvcConfigurer配置 | 全局配置 | ★★★☆☆ | | Filter过滤器 | 需要精细控制 | ★★★★☆ | | 网关层处理(如Nginx) | 基础设施层解决方案 | ★★★★☆ | ## 三、注解级解决方案 ### 3.1 方法级别注解 ```java @RestController @RequestMapping("/api") public class UserController { @CrossOrigin(origins = "https://frontend.com") @GetMapping("/users") public List<User> getUsers() { // ... } }
@CrossOrigin(origins = "https://frontend.com", maxAge = 3600, allowedHeaders = {"Content-Type", "Authorization"}) @RestController @RequestMapping("/api") public class ProductController { // 所有方法继承CORS配置 }
@CrossOrigin( origins = {"http://site1.com", "http://site2.com"}, // 允许的源 methods = {RequestMethod.GET, RequestMethod.POST}, // 允许的HTTP方法 allowedHeaders = "*", // 允许的请求头 exposedHeaders = {"X-Custom-Header"}, // 暴露的响应头 allowCredentials = "true", // 是否允许凭据 maxAge = 1800 // 预检请求缓存时间 )
@Configuration public class CorsConfig implements WebMvcConfigurer { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") .allowedOrigins("*") .allowedMethods("GET", "POST", "PUT", "DELETE") .allowedHeaders("*") .exposedHeaders("Authorization") .allowCredentials(false) .maxAge(3600); } }
# application.yml自定义配置示例 cors: allowed-origins: "https://frontend.com,http://localhost:8080" allowed-methods: "*" max-age: 1800
对应配置类:
@Configuration @ConfigurationProperties(prefix = "cors") public class CorsProperties { private String allowedOrigins; private String allowedMethods; private long maxAge; // getters/setters... }
@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", "*"); 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"); if ("OPTIONS".equalsIgnoreCase(((HttpServletRequest) req).getMethod())) { response.setStatus(HttpServletResponse.SC_OK); } else { chain.doFilter(req, res); } } }
@EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http.cors().configurationSource(corsConfigurationSource()) .and() // 其他安全配置... } @Bean CorsConfigurationSource corsConfigurationSource() { CorsConfiguration config = new CorsConfiguration(); config.setAllowedOrigins(Arrays.asList("https://trusted.com")); config.setAllowedMethods(Arrays.asList("*")); UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration("/**", config); return source; } }
@Profile("dev") @Configuration public class DevCorsConfig implements WebMvcConfigurer { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**").allowedOrigins("*"); } } @Profile("prod") @Configuration public class ProdCorsConfig implements WebMvcConfigurer { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/api/**") .allowedOrigins("https://production.com") .allowCredentials(true); } }
@Bean public WebMvcConfigurer corsConfigurer() { return new WebMvcConfigurer() { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") .allowedOrigins(dynamicOrigins()) // 其他配置... } }; } private String[] dynamicOrigins() { // 从数据库或配置中心获取 return originService.getAllowedOrigins(); }
@SpringBootTest class CorsTests { @Autowired private MockMvc mockMvc; @Test void testCorsHeaders() throws Exception { mockMvc.perform(options("/api/users") .header("Origin", "http://test.com") .header("Access-Control-Request-Method", "GET")) .andExpect(header().exists("Access-Control-Allow-Origin")); } }
问题现象 | 可能原因 | 解决方案 |
---|---|---|
预检请求返回403 | 未正确处理OPTIONS方法 | 配置中增加OPTIONS方法支持 |
凭证模式不生效 | allowCredentials与origin冲突 | 设置具体origin而非通配符 |
自定义头不被识别 | 未在exposedHeaders中声明 | 添加对应头到暴露头列表 |
部分接口配置不生效 | 过滤器顺序问题 | 调整@Order或FilterRegistrationBean |
生产环境安全原则
allowedOrigins("*")
allowCredentials
时必须指定具体originallowedMethods
到最小必要集合性能优化建议
maxAge
(建议1800秒以上)架构选择指南
graph TD A[需求场景] -->|简单API| B(注解方案) A -->|微服务架构| C(网关层统一处理) A -->|需要动态配置| D(数据库驱动过滤器)
SpringBoot 2.X提供了从注解到全局配置的完整CORS解决方案矩阵。建议: 1. 开发环境使用宽松配置加速开发 2. 生产环境遵循最小权限原则 3. 复杂场景考虑组合使用过滤器和网关方案
通过合理配置CORS,可以在保障安全的前提下实现现代Web应用的跨域需求。随着SpringBoot版本的更新,建议持续关注官方文档对CORS处理的改进。 “`
注:本文实际约4500字,可通过以下方式扩展: 1. 增加各方案的性能对比数据 2. 添加更详细的微服务集成案例 3. 补充GraphQL等特殊场景的处理方案 4. 加入与WebSocket的协同配置说明
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。