Spring-boot: FilterRegistrationBean should register multiple filter with the same attributes

Created on 16 Sep 2016  路  9Comments  路  Source: spring-projects/spring-boot

I have several filters with the same attributes, for example the same request matching pattern. For each filter I have to define corresponding FilterRegistrationBean.

I would like to reduce repeated code and define single FilterRegistrationBean to register multiple filters

declined

Most helpful comment

There's no need to repeat the code to configure the registration if you do something like this:

@Bean
public FilterRegistrationBean fooFilter() {
    return createFilterRegistration(new FooFilter());
}

@Bean
public FilterRegistrationBean barFilter() {
    return createFilterRegistration(new BarFilter());
}

private FilterRegistrationBean createFilterRegistration(Filter filter) {
    FilterRegistrationBean registration = new FilterRegistrationBean(filter);
    registration.addUrlPatterns("/foo");
    registration.addInitParameter("a", "alpha");
    return registration;
}

Supporting the registration of multiple filters from a single registration bean would require breaking API changes. Thanks for the suggestion but, given the alternative above, I don't think it's worth it.

All 9 comments

There's no need to repeat the code to configure the registration if you do something like this:

@Bean
public FilterRegistrationBean fooFilter() {
    return createFilterRegistration(new FooFilter());
}

@Bean
public FilterRegistrationBean barFilter() {
    return createFilterRegistration(new BarFilter());
}

private FilterRegistrationBean createFilterRegistration(Filter filter) {
    FilterRegistrationBean registration = new FilterRegistrationBean(filter);
    registration.addUrlPatterns("/foo");
    registration.addInitParameter("a", "alpha");
    return registration;
}

Supporting the registration of multiple filters from a single registration bean would require breaking API changes. Thanks for the suggestion but, given the alternative above, I don't think it's worth it.

I agree that this method will reduce code repeating to acceptable level, but it works only when filters are well known. What I really need is handle a family filters, for example:

@Bean FilterRegistrationBean(@Qualifier("foo") List<Filter> filters) {
    FilterRegistrationBean registration = new FilterRegistrationBean(filters);
    registration.addUrlPatterns("/foo");
    registration.addInitParameter("a", "alpha");
    return registration;
}

I'm confused. Above you said that "For each filter I have to define corresponding FilterRegistrationBean." If that's what you're doing today then there's nothing stopping you using the technique that I suggested.

If you need to customise the registration beans for an unknown number of filters, that's a very different requirement. Can you please clarify what you're doing at the moment and show the current repetition?

I'm thinking how to solve https://github.com/spring-projects/spring-security/issues/2948. Spring Security declares Filters as Spring beans and construct private filter chain, but Spring boot catches them and register as regular web filter (on pattern "/*")

I would like to catch somewhere in @Configuration org.springframework.security.web.FilterChainProxy, iterate over private filter chain and block registration all Spring Security filters as regular web filters.

It does not work because FilterRegistrationBean can block registration only single filter, not many

That's a _completely_ different scenario to what you described when you raised the issue.

A FilterRegistrationBean is automatically created for every Filter bean in the application context that doesn't already have a registration bean. In other words, there's no need for you to create them. Instead, for the problem that you're trying to solve, I'd use a BeanPostProcesser that post-processes FilterRegistrationBeans and disables them as appropriate.

My experiment shows that automatically created FilterRegistrationBeans are not handled by BeanPostProcesser. To register family of filters I have to manually configure family of FilterRegistrationBeans. For me that is the same scenario: I would like to disable family of filters by configure single bean

Sorry, I'd misremembered exactly how the automatic registration works. A FilterRegistrationBean is created for every Filter bean in the application context that doesn't already have a registration bean, however the registration beans that are created are not then added to the application context. That's why your BeanPostProcessor wasn't handling them.

Here's a BeanFactoryPostProcessor that will do what you want:

@Bean
public static BeanFactoryPostProcessor filterDisablingPostProcessor() {
    return new BeanFactoryPostProcessor() {

        @Override
        public void postProcessBeanFactory(
                ConfigurableListableBeanFactory beanFactory) throws BeansException {
            if (beanFactory instanceof DefaultListableBeanFactory) {
                for (String filterBeanName : beanFactory
                        .getBeanNamesForType(Filter.class, true, false)) {
                    if (disableFilter(filterBeanName, beanFactory)) {
                        BeanDefinition registrationDefinition = BeanDefinitionBuilder
                                .genericBeanDefinition(FilterRegistrationBean.class)
                                .addConstructorArgReference(filterBeanName)
                                .addConstructorArgValue(
                                        new ServletRegistrationBean[0])
                                .addPropertyValue("enabled", false)
                                .getBeanDefinition();
                        ((DefaultListableBeanFactory) beanFactory)
                                .registerBeanDefinition(
                                        filterBeanName + "Registration",
                                        registrationDefinition);
                    }
                }
            }
        }

        private boolean disableFilter(String filterBeanName,
                ConfigurableListableBeanFactory beanFactory) {
            return true;
        }

    };
}

As it stand it disables every filter, so you'll see output like this for a standard web app:

2016-09-28 10:19:10.775  INFO 73315 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Filter orderedRequestContextFilter was not registered (disabled)
2016-09-28 10:19:10.775  INFO 73315 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Filter orderedHiddenHttpMethodFilter was not registered (disabled)
2016-09-28 10:19:10.775  INFO 73315 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Filter orderedHttpPutFormContentFilter was not registered (disabled)
2016-09-28 10:19:10.775  INFO 73315 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Filter orderedCharacterEncodingFilter was not registered (disabled)

You can add logic to disableFilter to selectively disable filters as appropriate.

I know and apply that method but I'm not satisfied.

Work with BeanDefinition is low-level, type-check-less and hard. Work with plain objects is nice and natural. If only I could disable several filters with single shot...

What you are trying to do is low-level. Sorry, but I don't want to change a high-level API and add complexity to satisfy a low-level use case.

Was this page helpful?
0 / 5 - 0 ratings