禁用 WebFilter 的自动注册

Posted

技术标签:

【中文标题】禁用 WebFilter 的自动注册【英文标题】:Disable automatic registration of WebFilter 【发布时间】:2021-06-16 20:15:14 【问题描述】:

在 spring-boot 2.4 应用程序中,我有两个 SecurityWebFilterChains。对于其中一个,我想通过addFilterBefore()添加一些WebFilters。

@Configuration
@EnableWebFluxSecurity
class WebSecurityConfig 

    @Bean
    fun filter1(service: Service): WebFilter = Filter1(service)

    @Bean
    fun filter2(component: Component): WebFilter = Filter2(component)

    @Bean
    @Order(1)
    fun apiSecurityConfiguration(
        http: ServerHttpSecurity,
        filter1: WebFilter,
        filter2: WebFilter
    ): SecurityWebFilterChain = http
        .securityMatcher(pathMatchers("/path/**"))
        .addFilterBefore(filter1, SecurityWebFiltersOrder.AUTHENTICATION)
        .addFilterAt(filter2, SecurityWebFiltersOrder.AUTHENTICATION)
        .build()

    @Bean
    @Order(2)
    fun actuatorSecurityConfiguration(
        http: ServerHttpSecurity,
        reactiveAuthenticationManager: ReactiveAuthenticationManager
    ): SecurityWebFilterChain = http
        .securityMatcher(pathMatchers("/manage/**"))
        .authenticationManager(reactiveAuthenticationManager)
        .httpBasic  
        .build()


但是,由于这些 WebFilters 是作为 bean 创建的,它们会自动注册并应用于所有请求,似乎在安全链之外。

对于 servlet 过滤器,可以使用 FilterRegistrationBean 禁用此注册(请参阅 spring-boot documentation)。

反应式WebFilters 是否有类似的方法,还是我必须在这些过滤器中添加额外的 URL 过滤?

【问题讨论】:

为什么不将它们创建为 bean? 这些过滤器依赖于其他 bean,为简洁起见,示例中未显示。 然后在这些bean中自动装配并在实例化期间将它们输入过滤器的构造函数。 听起来可行,但也有点违背控制反转的原则。 好吧,您已经通过手动实例化和设置过滤器来忽略 IoC 的原则......所以......你开始了它,而不是我 :) 【参考方案1】:

要找到解决方案,我们首先必须更深入地研究弹簧的工作原理及其内部结构。

WebFilter 类型的所有 bean 都会自动添加到主 Web 处理过滤器链中。

请参阅spring boot documentation 的主题:

在应用程序上下文中找到的 WebFilter bean 将自动用于过滤每个交换。

即使您希望它们仅应用于特定的 spring-security 过滤器链,也会发生这种情况。

(恕我直言,重复使用FilterWebFilter 接口并且没有具有相同签名的特定于安全性的东西是弹簧安全性的一个缺陷。)

在代码中,相关部分在spring-web的WebHttpHandlerBuilder

public static WebHttpHandlerBuilder applicationContext(ApplicationContext context) 
    // ...

    List<WebFilter> webFilters = context
            .getBeanProvider(WebFilter.class)
            .orderedStream()
            .collect(Collectors.toList());
    builder.filters(filters -> filters.addAll(webFilters));

    // ...

然后在 spring-boot 的 HttpHandlerAutoConfiguration 中调用它来创建主 HttpHandler。

@Bean
public HttpHandler httpHandler(ObjectProvider<WebFluxProperties> propsProvider) 
    HttpHandler httpHandler = WebHttpHandlerBuilder.applicationContext(this.applicationContext).build();
    // ...
    return httpHandler;

为了防止将这些过滤器应用于所有交换,可以不将它们创建为 bean 并手动创建它们,如上面评论中所建议的那样。那么 BeanProvider 将不会找到它们并且不会将它们添加到 HttpHandler 中。但是,您离开 IoC 国家并失去过滤器的自动配置。当这些过滤器变得更复杂或拥有大量过滤器时,这并不理想。

因此,我的解决方案是为我的应用程序手动配置 HttpHandler,这不会将我的安全特定过滤器添加到全局过滤器链中。

为了完成这项工作,我首先为我的过滤器声明一个marker interface。

interface NonGlobalFilter

class MySecurityFilter : WebFilter, NonGlobalFilter 
  // ...

然后,在创建自定义 HttpHandler 的地方需要一个配置类。方便的是,WebHttpHandlerBuilder 有一个 method to manipulate its filter list 和一个消费者。

这将阻止 spring-boot 使用来自 HttpHandlerAutoConfiguration 的自己的 HttpHandler,因为它带有 @ConditionalOnMissingBean(HttpHandler.class) 注释。

@Configuration
class WebHttpHandlerConfiguration(
    private val applicationContext: ApplicationContext
) 
    @Bean
    fun httpHandler() = WebHttpHandlerBuilder
        .applicationContext(applicationContext)
        .filters 
            it.removeIf 
                webFilter -> webFilter is NonGlobalFilter
            
        
        .build()

就是这样!与往常一样,spring 提供了许多开箱即用的有用默认值,但是当它妨碍您时,将有一种方法可以根据需要进行调整。

【讨论】:

以上是关于禁用 WebFilter 的自动注册的主要内容,如果未能解决你的问题,请参考以下文章

如何在Edge中禁用自动更正?

如何用Python处理小程序功能

Win10 永久关闭自动更新,禁止windows10自动更新

Win10 永久关闭自动更新,禁止windows10自动更新

Win10 永久关闭自动更新,禁止windows10自动更新

禁用自动登录Windows 7(和缺少帐户图标)