今天遇到一个问题,就是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拦截器阻止请求。

posted on 2019-06-01 21:18  浮舟z  阅读(849)  评论(0编辑  收藏  举报