今天遇到一个问题,就是spring中配置cors没有效果。原来是因为被spring security拦截处理了。配置了CorsFilter在Spring security过滤器链之后,而通过WebMvcConfigurationSupport配置的CorsConfiguration则是Spring MVC中的, 更是在Spring security过滤器链的后面, 因此没有效果。而通过配置CorsConfigurationSource则会在Spring Security的过滤链中加上CORS过滤器。
Spring security的文档这样说:
Spring中添加CORS配置最简单的方法是通过CorsFilter, 也可以通过配置CorsConfigurationSource来使spring security使用cors, 这样会把CorsFilter加到Spring security的过滤器链中。
我的发现:
在Spring webFlux security中, 如果自己定义CorsFitler, 必须保证在Spring security的过滤器链之前, 这样才不会被Spring Security拦截处理, 从而没有了Cors过滤器。通过指定过滤器的Order为 @Order(Ordered.HIGHEST_PRECEDENCE)会在Spring security的过滤器链之前处理。
而在Spring Web Security 中则可以直接指定CorsFilter, Spring security会直接使用已有的CorsFilter。
https://docs.spring.io/spring-security/site/docs/4.2.x/reference/html/cors.html
Spring Web Flux Cors
看下Spring webflux security是如何把cors加到过滤器链中的:
在http.build()中
if (this.cors != null) {
this.cors.configure(this);
}
protected void configure(ServerHttpSecurity http) {
CorsWebFilter corsFilter = this.getCorsFilter();
if (corsFilter != null) {
http.addFilterAt(this.corsFilter, SecurityWebFiltersOrder.CORS);
}
}
private CorsWebFilter getCorsFilter() {
if (this.corsFilter != null) {
return this.corsFilter;
} else {
CorsConfigurationSource source = (CorsConfigurationSource)ServerHttpSecurity.this.getBeanOrNull(CorsConfigurationSource.class);
if (source == null) {
return null;
} else {
CorsProcessor processor = (CorsProcessor)ServerHttpSecurity.this.getBeanOrNull(CorsProcessor.class);
if (processor == null) {
processor = new DefaultCorsProcessor();
}
this.corsFilter = new CorsWebFilter(source, (CorsProcessor)processor);
return this.corsFilter;
}
}
}
而通过ServerHttpSecurity.this.getBeanOrNull()
方法获取容器中的bean, 从而在这里获取CorsConfigurationSource实例配置CORS。
Spring Web Security Cors
而在spring web security中似乎spring security是可以识别CorsFilter并使用的, 这里没有实际测试过。
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.cors().disable()
.httpBasic().disable()
.formLogin().disable()
.addFilterBefore(jwtAuthorizationFilter, UsernamePasswordAuthenticationFilter.class)
.authorizeRequests()
.antMatchers("/actuator/**").permitAll()
.antMatchers(
"/company/user/check/**",
).permitAll()
.anyRequest().authenticated()
.and()
.exceptionHandling().authenticationEntryPoint(defaultAuthenticationEntryPoint);
}
而在HttpSecurity类中
public CorsConfigurer<HttpSecurity> cors() throws Exception {
return (CorsConfigurer)this.getOrApply(new CorsConfigurer());
}
Spring web security CorsConfigurer中:
private CorsFilter getCorsFilter(ApplicationContext context) {
if (this.configurationSource != null) {
return new CorsFilter(this.configurationSource);
} else {
boolean containsCorsFilter = context.containsBeanDefinition("corsFilter");
if (containsCorsFilter) {
return (CorsFilter)context.getBean("corsFilter", CorsFilter.class);
} else {
boolean containsCorsSource = context.containsBean("corsConfigurationSource");
if (containsCorsSource) {
CorsConfigurationSource configurationSource = (CorsConfigurationSource)context.getBean("corsConfigurationSource", CorsConfigurationSource.class);
return new CorsFilter(configurationSource);
} else {
boolean mvcPresent = ClassUtils.isPresent("org.springframework.web.servlet.handler.HandlerMappingIntrospector", context.getClassLoader());
return mvcPresent ? CorsConfigurer.MvcCorsFilter.getMvcCorsFilter(context) : null;
}
}
}
}
首先检查CorsFilter, 然后检查CorsConfigurationSource bean。
此时直接设置CorsFilter是可以加到spring security过滤器链中的。
Spring mvc Cors
Spring MVC的文档这样说:
Spring MVC 的HandlerMapping实现内置支持CORS, 在成功映射一个请求到一个handler之后, HandlerMapping会检查CORS配置以采取下一步动作。
https://docs.spring.io/spring/docs/current/spring-framework-reference/web.html#mvc-cors-processing
我的发现:
Spring MVC会在找到handler后通过添加一个拦截器来检查CORS配置。
Spring MVC的CORS是在找到hander之后, 这个更是在Spring security的过滤器链之后, 从而cors没有效果。
下面来看一下Spring MVC中的CORS的实现。
DispatcherServlet调用AbstractHandlerMapping中的getHandler()方法:
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
Object handler = this.getHandlerInternal(request);
if (handler == null) {
handler = this.getDefaultHandler();
}
if (handler == null) {
return null;
} else {
if (handler instanceof String) {
String handlerName = (String)handler;
handler = this.obtainApplicationContext().getBean(handlerName);
}
HandlerExecutionChain executionChain = this.getHandlerExecutionChain(handler, request);
if (this.logger.isTraceEnabled()) {
this.logger.trace("Mapped to " + handler);
} else if (this.logger.isDebugEnabled() && !request.getDispatcherType().equals(DispatcherType.ASYNC)) {
this.logger.debug("Mapped to " + executionChain.getHandler());
}
if (CorsUtils.isCorsRequest(request)) {
CorsConfiguration globalConfig = this.corsConfigurationSource.getCorsConfiguration(request);
CorsConfiguration handlerConfig = this.getCorsConfiguration(handler, request);
CorsConfiguration config = globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig;
executionChain = this.getCorsHandlerExecutionChain(request, executionChain, config);
}
return executionChain;
}
}
自动加上一个cors的拦截器:
protected HandlerExecutionChain getCorsHandlerExecutionChain(HttpServletRequest request, HandlerExecutionChain chain, @Nullable CorsConfiguration config) {
if (CorsUtils.isPreFlightRequest(request)) {
HandlerInterceptor[] interceptors = chain.getInterceptors();
chain = new HandlerExecutionChain(new AbstractHandlerMapping.PreFlightHandler(config), interceptors);
} else {
chain.addInterceptor(new AbstractHandlerMapping.CorsInterceptor(config));
}
return chain;
}
private class CorsInterceptor extends HandlerInterceptorAdapter implements CorsConfigurationSource {
@Nullable
private final CorsConfiguration config;
public CorsInterceptor(@Nullable CorsConfiguration config) {
this.config = config;
}
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
return AbstractHandlerMapping.this.corsProcessor.processRequest(this.config, request, response);
}
@Nullable
public CorsConfiguration getCorsConfiguration(HttpServletRequest request) {
return this.config;
}
}
DefaultCorsProcessor
public boolean processRequest(@Nullable CorsConfiguration config, HttpServletRequest request, HttpServletResponse response) throws IOException {
if (!CorsUtils.isCorsRequest(request)) {
return true;
} else {
ServletServerHttpResponse serverResponse = new ServletServerHttpResponse(response);
if (this.responseHasCors(serverResponse)) {
logger.trace("Skip: response already contains \"Access-Control-Allow-Origin\"");
return true;
} else {
ServletServerHttpRequest serverRequest = new ServletServerHttpRequest(request);
if (WebUtils.isSameOrigin(serverRequest)) {
logger.trace("Skip: request is from same origin");
return true;
} else {
boolean preFlightRequest = CorsUtils.isPreFlightRequest(request);
if (config == null) {
if (preFlightRequest) {
this.rejectRequest(serverResponse);
return false;
} else {
return true;
}
} else {
return this.handleInternal(serverRequest, serverResponse, config, preFlightRequest);
}
}
}
}
}
dispatcherServlet中在真正invoke handler之前调用拦截器:
doDispatch方法:
HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if ((new ServletWebRequest(request, response)).checkNotModified(lastModified) && isGet) {
return;
}
}
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
从而通过加的cors拦截器阻止请求。