Spring cors

今天遇到一個問題,就是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 Web Flux Cors

看下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 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 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攔截器阻止請求。

相關文章
相關標籤/搜索