目录:
环境: SpringBoot 2.7.12
、Spring 5.3.27
SpringBoot 3.x
(Spring 6.x
)版本也会受影响。
CorsFilter 方式设置跨域
使用 SpringBoot
3.1.0
设置跨域时,想着直接把之前的代码拷贝过来直接用,心里美滋滋。
/**
* @author roc
* @date 2019/1/25
*/
@Configuration
public class CoreFilter {
@Bean
public CorsFilter corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration();
config.addAllowedOrigin("*");
config.setAllowCredentials(true);
config.addAllowedHeader("*");
config.addAllowedMethod("*");
source.registerCorsConfiguration("/**", config);
FilterRegistrationBean<CorsFilter> bean = new FilterRegistrationBean<>(new CorsFilter(source));
bean.setOrder(Ordered.HIGHEST_PRECEDENCE);
return new CorsFilter(source);
}
}
运行项目,开整。
- 启动没问题
Postman
测试接口通过Vue
调试接口,axios
一把梭- 运行项目
- Emmmm,浏览器访问接口提示跨域,且 Java 报错如下:
When allowCredentials is true, allowedOrigins cannot contain the special value "*" since that cannot be set on the
"Access-Control-Allow-Origin" response header.
To allow credentials to a set of origins, list them explicitly or consider using "allowedOriginPatterns" instead.
翻译如下:
错误原因就是字面意思:当 allowCredentials
为 true
时,allowedOrigins
不能包含特殊值 “*”
,因为它不能在 “Access-Control-Allow-Origin”
响应头中设置。
若要允许一组来源的凭据,请显式列出它们,或者考虑使用 “allowedOriginPatterns”
。
??? 这是啥时候有的限制,直接肝 Spring 源码(5.3.27
)。
最终在 org.springframework.web.cors.CorsConfiguration#validateAllowCredentials
类中发现了这一步校验:
/**
* Validate that when {@link #setAllowCredentials allowCredentials} is {@code true},
* {@link #setAllowedOrigins allowedOrigins} does not contain the special
* value {@code "*"} since in that case the "Access-Control-Allow-Origin"
* cannot be set to {@code "*"}.
* @throws IllegalArgumentException if the validation fails
* @since 5.3
*/
public void validateAllowCredentials() {
if (this.allowCredentials == Boolean.TRUE &&
this.allowedOrigins != null && this.allowedOrigins.contains(ALL)) {
throw new IllegalArgumentException(
"When allowCredentials is true, allowedOrigins cannot contain the special value \"*\" " +
"since that cannot be set on the \"Access-Control-Allow-Origin\" response header. " +
"To allow credentials to a set of origins, list them explicitly " +
"or consider using \"allowedOriginPatterns\" instead.");
}
}
从注释中可以发现,从
5.3
版本就有了这个限制了,所有后续的使用也将有所改变。
项目要显式使用估计不大可能,一大堆系统需要调用,挨个配置与维护着实有些麻烦(But,条件允许的情况下,还是尽可能的规范)。毕竟接口都有登录鉴权,问题不大;直接简单点,使用 allowedOriginPatterns
一把梭,代码更改如下:
/**
* @author roc
* @date 2019/1/25
*/
@Configuration
public class CoreFilter {
@Bean
public CorsFilter corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration();
config.addAllowedOriginPattern("*"); // 不是 addAllowedOrigin
config.setAllowCredentials(true);
config.addAllowedHeader("*");
config.addAllowedMethod("*");
source.registerCorsConfiguration("/**", config);
FilterRegistrationBean<CorsFilter> bean = new FilterRegistrationBean<>(new CorsFilter(source));
bean.setOrder(Ordered.HIGHEST_PRECEDENCE);
return new CorsFilter(source);
}
}
运行项目,开整。
- 启动没问题
Postman
测试接口通过Vue
调试接口,axios
一把梭- 运行项目,没问题,收工。
下面整理几个设置跨域的方式,如果有其他常用的方式也可以 call me。
补充:通过 addCorsMappings 实现跨域
@Configuration
public class CorsConfigurer implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**") // 拦截所有的请求
//.allowedOrigins("*")
.allowedOriginPatterns("*")
.allowCredentials(true)
.allowedMethods("*") // 允许跨域的方法,可以单独配置
.allowedHeaders("*"); // 允许跨域的请求头,可以单独配置
}
}
与使用 CorsFilter
方式一样,当 allowCredentials
为 true
时,不可以将 allowedOrigins
设置为 *
,必须用 allowedOriginPatterns
。否则最终也会到 CorsConfiguration#validateAllowCredentials
方法中的校验。
/**
* Validate that when {@link #setAllowCredentials allowCredentials} is {@code true},
* {@link #setAllowedOrigins allowedOrigins} does not contain the special
* value {@code "*"} since in that case the "Access-Control-Allow-Origin"
* cannot be set to {@code "*"}.
* @throws IllegalArgumentException if the validation fails
* @since 5.3
*/
public void validateAllowCredentials() {
if (this.allowCredentials == Boolean.TRUE &&
this.allowedOrigins != null && this.allowedOrigins.contains(ALL)) {
throw new IllegalArgumentException(
"When allowCredentials is true, allowedOrigins cannot contain the special value \"*\" " +
"since that cannot be set on the \"Access-Control-Allow-Origin\" response header. " +
"To allow credentials to a set of origins, list them explicitly " +
"or consider using \"allowedOriginPatterns\" instead.");
}
}
补充:通过 @CrossOrigin 实现跨域
@CrossOrigin
可以标注类和单个方法。
标注类:
该 Controller
请求路径下的所有方法都允许跨域。
@CrossOrigin // 类添加注解
@RestController
@RequestMapping("/test")
public class TestController {
@RequestMapping(value = "/test", method = RequestMethod.GET)
@ResponseBody
public String test() {
return "Success";
}
@RequestMapping(value = "/test1", method = RequestMethod.GET)
@ResponseBody
public String test1() {
return "Success";
}
}
/test/test
与 /test/test1
都允许跨域。
标注方法:
仅标注的方法允许跨域。
@RestController
@RequestMapping("/test")
public class TestController {
@CrossOrigin // 方法添加注解
@RequestMapping(value = "/test", method = RequestMethod.GET)
@ResponseBody
public String test() {
return "Success";
}
@RequestMapping(value = "/test1", method = RequestMethod.GET)
@ResponseBody
public String test1() {
return "Success";
}
}
/test/test
:允许跨域。/test/test1
:不允许跨域。