弹簧启动过滤顺序

如何指定弹簧启动过滤器的顺序?我需要在 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;
}
114462 次浏览

春天的伙计们又帮忙了。参见 https://github.com/spring-projects/spring-boot/issues/1640https://jira.spring.io/browse/SEC-2730

SpringSecurity 不会在 Filterbean 上设置一个订单 这意味着,当 Boot 创建一个 对于 FilterRegistrationBean,它获得默认顺序为 最低 _ 优先次序。

如果你想你自己的过滤器去春天安全的,你可以 为 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;
}

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

它也可以通过属性设置:

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

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

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

我的用例是一个自定义日志 javax.servlet.Filter,我想在任何 Spring Security 过滤器之前执行它; 然而,下面的步骤应该允许您将过滤器放置在现有 Spring 过滤器链的任何位置:

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

将您喜欢的远程调试器连接到应用程序,并在 org.springframework.security.web.FilterChainProxydoFilter(ServletRequest request, ServletResponse response)方法中设置断点。从 Spring Security 5.1.6开始,就是第311行。在调试器中,通过检查 this.additionalFilters找出现有的过滤器。在我的申请中,顺序是这样的:

0: WebAsyncManagerIntegrationFilter
1: SecurityContextPersistenceFilter
2: HeaderWriterFilter
...

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

您可能已经有了一个具有 @Override configure(HttpSecurity http)方法的 WebSecurityConfigurerAdapterHttpSecurity公开 addFilterBeforeaddFilterAfter方法,以允许您相对于链中的现有类放置筛选器。过滤器(实例)是这些方法的第一个参数,希望在前面或后面插入的过滤器类是第二个参数。

在我的例子中,我希望我的自定义日志过滤器是链中的第一个(我的代码片段是 Kotlin,我将把 Java 实现留给您) :

override fun configure(http: HttpSecurity) {
http
.addFilterBefore(MyCustomLoggingFilter(), WebAsyncManagerIntegrationFilter::class.java)
.authorizeRequests()
.antMatchers(
...
)
}

第三步: 利润!

使用上面步骤1中描述的调试方法来验证您的筛选器是否位于筛选器链中预期的位置。

希望这能帮到其他人。

请注意,当涉及到过滤器时,Spring Security 过滤器链是 没有。事实上,有一个完整的(非安全的)过滤器链在工作,其中一个过滤器是 DelegatingFilterProxy的一个实例,它委托给另一个叫做 FilterChainProxy的过滤器,它管理自己的过滤器子列表,所有过滤器都面向与安全相关的主题。此模式的优点是,所有安全过滤器都位于一个位置,并且相互之间排序正确。然而,如果您需要执行过滤器 之前之后所有安全过滤器,这对您一点帮助也没有。虽然确实可以通过选择列表中的第一个或最后一个过滤器来使用 HttpSecurity对象配置它,但这在逻辑上很奇怪,因为您的过滤器很可能与安全性毫无关系。如果在 DelegatingFilterProxy::doFilter(ServletRequest, ServletResponse, FilterChain)中设置断点,您将看到应用程序的整个过滤器链。如果愿意,您甚至可以钻入 FilterChainProxy以查找所有弹簧安全过滤器。然后,您可以打开相关的类一个接一个地查找它们的配置顺序。一旦你知道你的过滤器应该放在哪里,你就可以对它进行注释。

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

在第一种情况下,它是一个错误配置,Spring 的文档有一个特别的提示:

不能通过用@Order 注释 Filter 的 bean 方法来配置 Filter 的顺序。

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

Web = debug

应用性能中,您可以看到注册过滤器的详细信息,包括它们的顺序和启动时的 URL 模式。