springSecurity自定义过滤器不生效问题排查

在使用springSecurity过滤器的过程中,由于需要自定义一个过滤器处理数据问题。代码如下:

过滤器定义:

public class AuthRequestParamFiler extends GenericFilterBean {

    private static final CoreLogger LOGGER = CoreLoggerFactory.getLogger(AuthRequestParamFiler.class);

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest httpRequest = (HttpServletRequest) request;

        String path = httpRequest.getServletPath();
        if(path.equals("/oauth/token")) {
            LOGGER.info("AuthRequestFiler 过滤器, path:{}", path);
            Map<String, String> requestMap = HttpUtil.getRequestPostMap(httpRequest);

            AuthParamRequestWrapper contentCachingRequestWrapper = new AuthParamRequestWrapper(httpRequest, requestMap);

            chain.doFilter(contentCachingRequestWrapper, response);
        } else {
            chain.doFilter(request, response);
        }
    }
}

过滤器注册:

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class AuthWebServiceSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private AuthUserDetailsService authUserDetailsService;

    /**
     * 对请求进行鉴权的配置
     * https://zhuanlan.zhihu.com/p/181156708
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.userDetailsService(authUserDetailsService);
        http.authorizeRequests()
                //.antMatchers().permitAll()
                .anyRequest().authenticated()
                .and()
                // 暂时关闭CSRF校验,允许get请求登出
                .csrf().disable()
                .addFilterBefore(new AuthRequestParamFiler(), UsernamePasswordAuthenticationFilter.class);
    }
}

 但是在执行中却没有执行该过滤器。

首先排查代码问题。通过debug可以看出,服务一共构造了两个WebSecurityConfigurerAdapter类,每一个类都有自己的DefaultSecurityFilterChain调用链。

第一个:

 第二个:

 看得出来,我的AuthRequestParamFiler已经注册到了这个过滤器链路里面。但是,在实际执行中却没有执行。

但是我的代码能真长的返回。由此可以看出,是没有执行到这个拦截器就已经返回了。

通过查询FilterChainProxy的代码我们可以看到在获取调用链的时候,只要匹配到一个就直接返回了。这就有可能会导致如果我的自定义拦截器排在了其他拦截器的后面,会先返回其他的拦截器。

public class FilterChainProxy extends GenericFilterBean {	
    private List<Filter> getFilters(HttpServletRequest request) {
		for (SecurityFilterChain chain : filterChains) {
			if (chain.matches(request)) {
				return chain.getFilters();
			}
		}

		return null;
	}
}

在spring的网站中也有提到:

 所以应该是我的过滤器排在系统默认的拦截器之后的原因导致的。这个排序是在WebSecurityConfiguration中实现的。AutowiredWebSecurityConfigurersIgnoreParents类获取了所有定义的WebSecurityConfigurer继承类。setFilterChainProxySecurityConfigurer()方法对传入的参数进行了排序。排序方式是按照@Ordered配置的大小来决定的。

 

@Configuration(proxyBeanMethods = false)
public class WebSecurityConfiguration implements ImportAware, BeanClassLoaderAware {
	private WebSecurity webSecurity;

	private Boolean debugEnabled;

	private List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers;

	private ClassLoader beanClassLoader;

	@Autowired(required = false)
	private ObjectPostProcessor<Object> objectObjectPostProcessor;

	@Bean
	public static AutowiredWebSecurityConfigurersIgnoreParents autowiredWebSecurityConfigurersIgnoreParents(
			ConfigurableListableBeanFactory beanFactory) {
        //这个方法里面会获取所有的 WebSecurityConfigurer 类
		return new AutowiredWebSecurityConfigurersIgnoreParents(beanFactory);
	}


	@Autowired(required = false)
	public void setFilterChainProxySecurityConfigurer(
			ObjectPostProcessor<Object> objectPostProcessor,
			@Value("#{@autowiredWebSecurityConfigurersIgnoreParents.getWebSecurityConfigurers()}") List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers)
			throws Exception {
		webSecurity = objectPostProcessor
				.postProcess(new WebSecurity(objectPostProcessor));
		if (debugEnabled != null) {
			webSecurity.debug(debugEnabled);
		}
        //这里进行了排序
		webSecurityConfigurers.sort(AnnotationAwareOrderComparator.INSTANCE);

		Integer previousOrder = null;
		Object previousConfig = null;
		for (SecurityConfigurer<Filter, WebSecurity> config : webSecurityConfigurers) {
			Integer order = AnnotationAwareOrderComparator.lookupOrder(config);
			if (previousOrder != null && previousOrder.equals(order)) {
				throw new IllegalStateException(
						"@Order on WebSecurityConfigurers must be unique. Order of "
								+ order + " was already used on " + previousConfig + ", so it cannot be used on "
								+ config + " too.");
			}
			previousOrder = order;
			previousConfig = config;
		}
		for (SecurityConfigurer<Filter, WebSecurity> webSecurityConfigurer : webSecurityConfigurers) {
			webSecurity.apply(webSecurityConfigurer);
		}
		this.webSecurityConfigurers = webSecurityConfigurers;
	}

}

通过查看另外一个WebSecurityConfigurer的继承类AuthorizationServerSecurityConfiguration

@Configuration
@Order(0)
@Import({ ClientDetailsServiceConfiguration.class, AuthorizationServerEndpointsConfiguration.class })
public class AuthorizationServerSecurityConfiguration extends WebSecurityConfigurerAdapter {
    ......
}

 可以看出这个的配置的序号是0。所以我们自定义的AuthWebServiceSecurityConfig的序号要比0小。

注意:一旦做了这个配置就会导致原来的oauth2的Filer无法使用,进而导致无法使用oauth2的授权接口。