Springboot升级至2.4.0中出现的跨域问题分析及修改方案
问题
Springboot升级至2.4.0中出现的跨域问题。
在Springboot2.4.0版本之前使用的是2.3.5.RELEASE,对应的Spring版本为5.2.10.RELEASE。
升级至2.4.0后,对应的Spring版本为5.3.1。
Springboot2.3.5.RELEASE时,我们可以使用CorsFilter设置跨域。
分析
版本2.3.5.RELEASE设置跨域
设置代码如下:
@Configuration
publicclassResourcesConfigimplementsWebMvcConfigurer{
@Bean
publicCorsFiltercorsFilter(){
UrlBasedCorsConfigurationSourcesource=newUrlBasedCorsConfigurationSource();
CorsConfigurationconfig=newCorsConfiguration();
config.setAllowCredentials(true);
//允许访问的客户端域名
config.addAllowedOrigin("*");
//允许服务端访问的客户端请求头
config.addAllowedHeader("*");
//允许访问的方法名,GETPOST等
config.addAllowedMethod("*");
//对接口配置跨域设置
source.registerCorsConfiguration("/**",config);
returnnewCorsFilter(source);
}
}
是允许使用*设置允许的Origin。
这里我们看一下类CorsFilter的源码,5.3.x版本开始,针对CorsConfiguration新增了校验
5.3.x源码分析CorsFilter
/**
*{@linkjavax.servlet.Filter}tohandleCORSpre-flightrequestsandintercept
*CORSsimpleandactualrequestswitha{@linkCorsProcessor},andtoupdate
*theresponse,e.g.withCORSresponseheaders,basedonthepolicymatched
*throughtheprovided{@linkCorsConfigurationSource}.
*
*ThisisanalternativetoconfiguringCORSintheSpringMVCJavaconfig
*andtheSpringMVCXMLnamespace.Itisusefulforapplicationsdepending
*onlyonspring-web(notonspring-webmvc)orforsecurityconstraintsthat
*requireCORScheckstobeperformedat{@linkjavax.servlet.Filter}level.
*
*
Thisfiltercouldbeusedinconjunctionwith{@linkDelegatingFilterProxy}
*inordertohelpwithitsinitialization.
*
*@authorSebastienDeleuze
*@since4.2
*@seeCORSW3Crecommendation
*@seeUrlBasedCorsConfigurationSource
*/
publicclassCorsFilterextendsOncePerRequestFilter{
privatefinalCorsConfigurationSourceconfigSource;
privateCorsProcessorprocessor=newDefaultCorsProcessor();
/**
*Constructoracceptinga{@linkCorsConfigurationSource}usedbythefilter
*tofindthe{@linkCorsConfiguration}touseforeachincomingrequest.
*@seeUrlBasedCorsConfigurationSource
*/
publicCorsFilter(CorsConfigurationSourceconfigSource){
Assert.notNull(configSource,"CorsConfigurationSourcemustnotbenull");
this.configSource=configSource;
}
/**
*Configureacustom{@linkCorsProcessor}tousetoapplythematched
*{@linkCorsConfiguration}forarequest.
*Bydefault{@linkDefaultCorsProcessor}isused.
*/
publicvoidsetCorsProcessor(CorsProcessorprocessor){
Assert.notNull(processor,"CorsProcessormustnotbenull");
this.processor=processor;
}
@Override
protectedvoiddoFilterInternal(HttpServletRequestrequest,HttpServletResponseresponse,
FilterChainfilterChain)throwsServletException,IOException{
CorsConfigurationcorsConfiguration=this.configSource.getCorsConfiguration(request);
booleanisValid=this.processor.processRequest(corsConfiguration,request,response);
if(!isValid||CorsUtils.isPreFlightRequest(request)){
return;
}
filterChain.doFilter(request,response);
}
}
类CorsFilter继承自OncePerRequestFilter,doFilterInternal方法会被执行。类中还创建了一个默认的处理类DefaultCorsProcessor,doFilterInternal调用this.processor.processRequest
往下
processRequest
@Override
@SuppressWarnings("resource")
publicbooleanprocessRequest(@NullableCorsConfigurationconfig,HttpServletRequestrequest,
HttpServletResponseresponse)throwsIOException{
CollectionvaryHeaders=response.getHeaders(HttpHeaders.VARY);
if(!varyHeaders.contains(HttpHeaders.ORIGIN)){
response.addHeader(HttpHeaders.VARY,HttpHeaders.ORIGIN);
}
if(!varyHeaders.contains(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD)){
response.addHeader(HttpHeaders.VARY,HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD);
}
if(!varyHeaders.contains(HttpHeaders.ACCESS_CONTROL_REQUEST_HEADERS)){
response.addHeader(HttpHeaders.VARY,HttpHeaders.ACCESS_CONTROL_REQUEST_HEADERS);
}
if(!CorsUtils.isCorsRequest(request)){
returntrue;
}
if(response.getHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN)!=null){
logger.trace("Skip:responsealreadycontains\"Access-Control-Allow-Origin\"");
returntrue;
}
booleanpreFlightRequest=CorsUtils.isPreFlightRequest(request);
if(config==null){
if(preFlightRequest){
rejectRequest(newServletServerHttpResponse(response));
returnfalse;
}
else{
returntrue;
}
}
returnhandleInternal(newServletServerHttpRequest(request),newServletServerHttpResponse(response),config,preFlightRequest);
}
进入最后一行
handleInternal
/**
*Handlethegivenrequest.
*/
protectedbooleanhandleInternal(ServerHttpRequestrequest,ServerHttpResponseresponse,
CorsConfigurationconfig,booleanpreFlightRequest)throwsIOException{
StringrequestOrigin=request.getHeaders().getOrigin();
StringallowOrigin=checkOrigin(config,requestOrigin);
(省略...)
response.flush();
returntrue;
}
查看方法checkOrigin
checkOrigin
@Nullable
protectedStringcheckOrigin(CorsConfigurationconfig,@NullableStringrequestOrigin){
returnconfig.checkOrigin(requestOrigin);
}
Goon
checkOrigin
/**
*Checktheoriginoftherequestagainsttheconfiguredallowedorigins.
*@paramrequestOrigintheorigintocheck
*@returntheorigintousefortheresponse,or{@codenull}which
*meanstherequestoriginisnotallowed
*/
@Nullable
publicStringcheckOrigin(@NullableStringrequestOrigin){
if(!StringUtils.hasText(requestOrigin)){
returnnull;
}
if(!ObjectUtils.isEmpty(this.allowedOrigins)){
if(this.allowedOrigins.contains(ALL)){
validateAllowCredentials();
returnALL;
}
for(StringallowedOrigin:this.allowedOrigins){
if(requestOrigin.equalsIgnoreCase(allowedOrigin)){
returnrequestOrigin;
}
}
}
if(!ObjectUtils.isEmpty(this.allowedOriginPatterns)){
for(OriginPatternp:this.allowedOriginPatterns){
if(p.getDeclaredPattern().equals(ALL)||p.getPattern().matcher(requestOrigin).matches()){
returnrequestOrigin;
}
}
}
returnnull;
}
方法validateAllowCredentials
validateAllowCredentials
/**
*Validatethatwhen{@link#setAllowCredentialsallowCredentials}istrue,
*{@link#setAllowedOriginsallowedOrigins}doesnotcontainthespecial
*value{@code"*"}sinceinthatcasethe"Access-Control-Allow-Origin"
*cannotbesetto{@code"*"}.
*@throwsIllegalArgumentExceptionifthevalidationfails
*@since5.3
*/
publicvoidvalidateAllowCredentials(){
if(this.allowCredentials==Boolean.TRUE&&
this.allowedOrigins!=null&&this.allowedOrigins.contains(ALL)){
thrownewIllegalArgumentException(
"WhenallowCredentialsistrue,allowedOriginscannotcontainthespecialvalue\"*\""+
"sincethatcannotbesetonthe\"Access-Control-Allow-Origin\"responseheader."+
"Toallowcredentialstoasetoforigins,listthemexplicitly"+
"orconsiderusing\"allowedOriginPatterns\"instead.");
}
看一下ALL是什么
/**Wildcardrepresentingallorigins,methods,orheaders.*/ publicstaticfinalStringALL="*";
所以如果使用2.4.0版本,还是设置*的话,访问API接口就会报错:
异常
java.lang.IllegalArgumentException:WhenallowCredentialsistrue,allowedOriginscannotcontainthespecialvalue"*"sincethatcannotbesetonthe"Access-Control-Allow-Origin"responseheader.Toallowcredentialstoasetoforigins,listthemexplicitlyorconsiderusing"allowedOriginPatterns"instead. atorg.springframework.web.cors.CorsConfiguration.validateAllowCredentials(CorsConfiguration.java:457) atorg.springframework.web.cors.CorsConfiguration.checkOrigin(CorsConfiguration.java:561) atorg.springframework.web.cors.DefaultCorsProcessor.checkOrigin(DefaultCorsProcessor.java:174) atorg.springframework.web.cors.DefaultCorsProcessor.handleInternal(DefaultCorsProcessor.java:116) atorg.springframework.web.cors.DefaultCorsProcessor.processRequest(DefaultCorsProcessor.java:95) atorg.springframework.web.filter.CorsFilter.doFilterInternal(CorsFilter.java:87) atorg.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) atorg.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336) atorg.springframework.security.web.header.HeaderWriterFilter.doHeadersAfter(HeaderWriterFilter.java:90) atorg.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:75) atorg.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) atorg.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336) atorg.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:110) atorg.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:80) atorg.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336) atorg.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:55) atorg.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) atorg.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336) atorg.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:211) atorg.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:183) atorg.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:358) atorg.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:271) atorg.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) atorg.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) atorg.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) atorg.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) atorg.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) atorg.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) atorg.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) atorg.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) atorg.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) atorg.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) atorg.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) atorg.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) atorg.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) atorg.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) atorg.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:202) atorg.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:97) atorg.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:542) atorg.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:143) atorg.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92) atorg.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:78) atorg.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343) atorg.apache.coyote.http11.Http11Processor.service(Http11Processor.java:374) atorg.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65) atorg.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:868) atorg.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1590) atorg.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) atjava.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) atjava.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) atorg.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) atjava.lang.Thread.run(Thread.java:748)
修改方式
方式1
不使用CorsFilter,直接重写方法addCorsMappings:
@Configuration
publicclassResourcesConfigimplementsWebMvcConfigurer{
/**
*跨域配置
*/
@Override
publicvoidaddCorsMappings(CorsRegistryregistry){
//对那些请求路径进行跨域处理
registry.addMapping("/**")
//允许的请求头,默认允许所有的请求头
.allowedHeaders("*")
//允许的方法,默认允许GET、POST、HEAD
.allowedMethods("*")
//探测请求有效时间,单位秒
.maxAge(1800)
//支持的域
.allowedOrigins("*");
}
}
方式2
继续使用CorsFilter,使用官方推荐的allowedOriginPatterns:
application.yml中新增配置:
(注:这里只是举个栗子…Origin的处理)
#项目相关配置
project:
uiPort:8082
basePath:http://localhost:${project.uiPort}
@Configuration
publicclassResourcesConfigimplementsWebMvcConfigurer{
@Value("${project.basePath}")
privateStringbasePath;
@Bean
publicCorsFiltercorsFilter(){
UrlBasedCorsConfigurationSourcesource=newUrlBasedCorsConfigurationSource();
CorsConfigurationconfig=newCorsConfiguration();
config.setAllowCredentials(true);
//允许访问的客户端域名
ListallowedOriginPatterns=newArrayList<>();
allowedOriginPatterns.add(basePath);
config.setAllowedOriginPatterns(allowedOriginPatterns);
//config.addAllowedOrigin(serverPort);
//允许服务端访问的客户端请求头
config.addAllowedHeader("*");
//允许访问的方法名,GETPOST等
config.addAllowedMethod("*");
//对接口配置跨域设置
source.registerCorsConfiguration("/**",config);
returnnewCorsFilter(source);
}
}
到此这篇关于Springboot升级至2.4.0中出现的跨域问题分析及修改方案的文章就介绍到这了,更多相关Springboot2.4.0跨域内容请搜索毛票票以前的文章或继续浏览下面的相关文章希望大家以后多多支持毛票票!