在spring-boot中过滤顺序

时间:2014-09-21 09:41:08

标签: java spring spring-mvc spring-boot

如何在spring-boot中指定过滤器的顺序?我需要在Spring Security过滤器之后插入我的MDC过滤器。我几乎尝试了所有东西,但我的过滤器总是第一个这不起作用:

@Bean
@Order(Ordered.LOWEST_PRECEDENCE)
public UserInsertingMdcFilter userInsertingMdcFilter() {
    return new UserInsertingMdcFilter();
}

这也不起作用:

@Bean
public FilterRegistrationBean userInsertingMdcFilterRegistrationBean() {
    FilterRegistrationBean registrationBean = new FilterRegistrationBean();
    UserInsertingMdcFilter userFilter = new UserInsertingMdcFilter();
    registrationBean.setFilter(userFilter);
    registrationBean.setOrder(Integer.MAX_VALUE);
    return registrationBean;
}

5 个答案:

答案 0 :(得分:37)

来自Spring的家伙再次帮助。请参阅https://github.com/spring-projects/spring-boot/issues/1640https://jira.spring.io/browse/SEC-2730

  

Spring Security没有在Filter bean上设置它的顺序   创建。这意味着,当Boot正在创建时   FilterRegistrationBean为它,它获取默认顺序   LOWEST_PRECEDENCE。

     

如果您希望自己的过滤器能够使用Spring Security,那么您可以   为Spring Security创建自己的注册过滤器并指定   订单。

所以我的问题的答案是:

@Bean
public FilterRegistrationBean securityFilterChain(@Qualifier(AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME) Filter securityFilter) {
    FilterRegistrationBean registration = new FilterRegistrationBean(securityFilter);
    registration.setOrder(Integer.MAX_VALUE - 1);
    registration.setName(AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME);
    return registration;
}

@Bean
public FilterRegistrationBean userInsertingMdcFilterRegistrationBean() {
    FilterRegistrationBean registrationBean = new FilterRegistrationBean();
    UserInsertingMdcFilter userFilter = new UserInsertingMdcFilter();
    registrationBean.setFilter(userFilter);
    registrationBean.setOrder(Integer.MAX_VALUE);
    return registrationBean;
}

答案 1 :(得分:12)

这是在Spring Boot 1.2中修复的。现在,安全链默认为订单0

也可以通过属性设置:

security.filter-order=0 # Security filter chain order.

https://github.com/spring-projects/spring-boot/issues/1640

答案 2 :(得分:1)

请注意,Spring Security 过滤器链并非涉及过滤器的全部内容。事实上,有一个完整的(非安全)过滤器链在起作用,其中一个过滤器是 DelegatingFilterProxy 的一个实例,它 (nomen est omen) 委托给另一个名为 FilterChainProxy 的过滤器来管理它的自己的过滤器子列表,全部面向安全相关主题。这种模式的优点是所有安全过滤器都在一个地方并且相互正确排序。但是,如果您需要在beforeafter 所有 安全过滤器执行过滤器,这根本无济于事。虽然您可以使用 HttpSecurity 对象通过选择该列表中的第一个或最后一个过滤器来配置它,但这在逻辑上很奇怪,因为您的过滤器很可能与安全无关。如果您在 DelegatingFilterProxy::doFilter(ServletRequest, ServletResponse, FilterChain) 中设置断点,您将看到应用程序的整个过滤器链。如果您愿意,您甚至可以深入到 FilterChainProxy 中查找所有 spring 安全过滤器。然后就可以一一打开相关的类,找出它们配置的命令。一旦您知道您的过滤器应该在哪里,您就可以对其进行注释。

例如,如果您需要配置一个日志过滤器并且您想要记录所有安全故障(需要在 FilterChainProxy 之前)但还需要一个有用的跟踪 ID(需要在 LazyTracingFilter 之后配置了 TraceHttpAutoConfiguration.TRACING_FILTER_ORDER = Ordered.HIGHEST_PRECEDENCE + 5 的默认顺序),您可以使用 @Order(Ordered.HIGHEST_PRECEDENCE + 6) 注释过滤器。

答案 3 :(得分:0)

这是与Spring Boot 2 / Spring Security 5兼容的答案,它将允许您将过滤器插入过滤器链中的任意位置。

我的用例是我想在任何Spring Security过滤器之前执行的自定义日志记录javax.servlet.Filter;但是,以下步骤应允许您将过滤器放置在现有的Spring过滤器链中的任何位置:

步骤1:找出现有设置中Spring过滤器的顺序。

将您喜欢的远程调试器连接到应用程序,并在doFilter(ServletRequest request, ServletResponse response)的{​​{1}}方法中设置一个断点。从Spring Security 5.1.6开始,即第311行。在调试器中,通过检查org.springframework.security.web.FilterChainProxy找出现有过滤器。在我的应用程序中,顺序类似于:

this.additionalFilters

步骤2:使用Spring的WebSecurityConfigurerAdapter和HttpSecurity将过滤器插入所需的位置

您可能已经拥有使用0: WebAsyncManagerIntegrationFilter 1: SecurityContextPersistenceFilter 2: HeaderWriterFilter ... 方法的WebSecurityConfigurerAdapter@Override configure(HttpSecurity http)公开了HttpSecurityaddFilterBefore方法,使您可以相对于链中现有的类放置过滤器。您的过滤器(实例)是这些方法的第一个参数,而您想要插入的过滤器的类是第二个参数。

就我而言,我希望自定义日志记录过滤器成为链中的第一个(我的代码段是Kotlin,我将Java实现留给您):

addFilterAfter

第3步:获利!

使用上面第1步中描述的调试方法来验证您的过滤器是否在过滤器链中的预期位置。

希望这可以帮助其他人。

答案 4 :(得分:0)

在你的第一种情况下,这是一个错误配置,spring的文档有特别提醒:

<块引用>

不能通过使用 @Order 注释其 bean 方法来配置过滤器的顺序。

你可以从中找到:https://docs.spring.io/spring-boot/docs/current/reference/html/spring-boot-features.html#boot-features-embedded-container-servlets-filters-listeners-beans