您好,登录后才能下订单哦!
密码登录
登录注册
点击 登录注册 即表示同意《亿速云用户服务条款》
# 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进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。