一、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")
四、最佳实践建议
- 生产环境不要使用通配符*:明确指定允许的源
- 合理限制http方法:只开放必要的方法(get/post等)
- 考虑使用环境变量:动态配置允许的源
@value("${cors.allowed.origins}")
private string[] allowedorigins;
// 在配置中使用
.allowedorigins(allowedorigins)
- 结合安全框架:spring security项目使用专门的cors配置
- 测试不同场景:简单请求和预检请求都要测试
五、完整配置示例
@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:
- 简单场景:使用
@crossorigin注解 - 统一配置:实现
webmvcconfigurer的addcorsmappings方法 - 底层控制:配置
corsfilter - 安全项目:结合spring security的
cors()配置 - 动态需求:手动设置响应头
根据项目需求选择合适的方式,并遵循安全最佳实践,可以有效地解决跨域问题,同时保证应用的安全性。
以上就是springboot处理跨域请求(cors)的五种方式的详细内容,更多关于springboot处理跨域请求的资料请关注代码网其它相关文章!
发表评论