今天遇到一個問題,就是spring中配置cors沒有效果。原來是由於被spring security攔截處理了。配置了CorsFilter在Spring security過濾器鏈以後,而經過WebMvcConfigurationSupport配置的CorsConfiguration則是Spring MVC中的, 更是在Spring security過濾器鏈的後面, 所以沒有效果。而經過配置CorsConfigurationSource則會在Spring Security的過濾鏈中加上CORS過濾器。html
Spring security的文檔這樣說:
Spring中添加CORS配置最簡單的方法是經過CorsFilter, 也能夠經過配置CorsConfigurationSource來使spring security使用cors, 這樣會把CorsFilter加到Spring security的過濾器鏈中。web
個人發現:
在Spring webFlux security中, 若是本身定義CorsFitler, 必須保證在Spring security的過濾器鏈以前, 這樣纔不會被Spring Security攔截處理, 從而沒有了Cors過濾器。經過指定過濾器的Order爲 @Order(Ordered.HIGHEST_PRECEDENCE)會在Spring security的過濾器鏈以前處理。spring
而在Spring Web Security 中則能夠直接指定CorsFilter, Spring security會直接使用已有的CorsFilter。
https://docs.spring.io/spring-security/site/docs/4.2.x/reference/html/cors.htmlmvc
看下Spring webflux security是如何把cors加到過濾器鏈中的:
在http.build()中app
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。cors
而在spring web security中彷佛spring security是能夠識別CorsFilter並使用的, 這裏沒有實際測試過。async
@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類中ide
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過濾器鏈中的。ui
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攔截器阻止請求。