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
Comment From: wilkinsona
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.
Comment From: michaldo
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;
}
Comment From: wilkinsona
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?
Comment From: michaldo
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
Comment From: wilkinsona
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.
Comment From: michaldo
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
Comment From: wilkinsona
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.
Comment From: michaldo
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...
Comment From: wilkinsona
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.
Comment From: VictorZZZZ
Hello!
I don't know if I can write here my problem, but I'll try. I need to make 2 FilterRegistrationBean in my application, and it requires FilterRegistrationBean to depend on two different bbjects of one class like
@Bean("customerFilter")
public FilterRegistrationBean<JwtFilter> jwtFilter() {
final KeycloakJwtHelper keycloakJwtHelper = new KeycloakJwtHelper(n, e);
FilterRegistrationBean<JwtFilter> filter= new FilterRegistrationBean<>();
filter.setFilter(new JwtFilter(keycloakJwtHelper));
filter.addUrlPatterns("/v1/*");
return filter;
}
@Bean("adminFilter")
public FilterRegistrationBean<JwtFilter> jwtFilter() {
final KeycloakJwtHelper keycloakJwtHelper = new KeycloakJwtHelper(c, c);
FilterRegistrationBean<JwtFilter> filter= new FilterRegistrationBean<>();
filter.setFilter(new JwtFilter(keycloakJwtHelper));
filter.addUrlPatterns("/admin/*");
return filter;
}
But it is not working. I Have in spring context 2 different beans customerFilter and adminFilter
but when I call a rest method /admin/something then required filter is not working.
I solved my problem by exending JwtFilter by JwtAdminFilter and then it works. But JwtAdminFilter don't do something new than JwtFilter. So it looks like crutch
Comment From: wilkinsona
@VictorZZZZ that looks like a different problem. From what you've shared thus far, I can't see why adminFilter would not be part of the filter chain for requests to /admin/something. Please open a new issue with a complete yet minimal sample that reproduces the problem and we can take a look.
Comment From: VictorZZZZ
@VictorZZZZ that looks like a different problem. From what you've shared thus far, I can't see why
adminFilterwould not be part of the filter chain for requests to/admin/something. Please open a new issue with a complete yet minimal sample that reproduces the problem and we can take a look.
In this case I have a KeycloakJwtHelper depending on different parameters in each method. Ok, i'll open a new Issue.