Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce a mechanism to disable existing filters/servlets beans #2173

Closed
cemo opened this issue Dec 16, 2014 · 28 comments
Closed

Introduce a mechanism to disable existing filters/servlets beans #2173

cemo opened this issue Dec 16, 2014 · 28 comments
Labels
type: documentation A documentation update
Milestone

Comments

@cemo
Copy link
Contributor

cemo commented Dec 16, 2014

It is quite possible to include a library which is exposing a Filter/Servlet bean. Please introduce a mechanism for exclusion.

Here is the relevant section of code:

org.springframework.boot.context.embedded.ServletContextInitializerBeans#addAdaptableBeans

    private void addAdaptableBeans(ListableBeanFactory beanFactory) {
        MultipartConfigElement multipartConfig = getMultipartConfig(beanFactory);
        addAsRegistrationBean(beanFactory, Servlet.class, new ServletRegistrationBeanAdapter(multipartConfig));
        addAsRegistrationBean(beanFactory, Filter.class, new FilterRegistrationBeanAdapter());
        for (Class<?> listenerType : ServletListenerRegistrationBean.getSupportedTypes()) {
            addAsRegistrationBean(beanFactory, EventListener.class, (Class<EventListener>) listenerType, new ServletListenerRegistrationBeanAdapter());
        }
    }

#2171 is also related to this issue.

@philwebb philwebb added the type: enhancement A general enhancement label Dec 16, 2014
@philwebb
Copy link
Member

Do you have any specific examples of libraries that are adding Filters/Servlets?

@philwebb philwebb added this to the 1.2.2 milestone Dec 16, 2014
@philwebb
Copy link
Member

OK, ignore that last comment. I see #2171 is related.

@dsyer
Copy link
Member

dsyer commented Dec 17, 2014

There is a mechanism for exclusion, if you have an immediate problem and just need the solution. You create a *RegistrationBean and set it to enabled=false.

@cemo
Copy link
Contributor Author

cemo commented Dec 17, 2014

@dsyer, Please correct me If I am wrong. You mean that I should add another FilterRegistrationBean with same filter name. This will cause skipping new created FilterRegistrationBean but not actual one. Right?

@dsyer
Copy link
Member

dsyer commented Dec 17, 2014

That wasn't what I meant. Why not use the enabled flag (it's just a Boolean property in the FRB)?

@cemo
Copy link
Contributor Author

cemo commented Dec 17, 2014

Because it is not a FilterRegistrationBean. It is a plain Filter.

@Bean(name=AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME)
    public Filter springSecurityFilterChain() throws Exception {
        boolean hasConfigurers = webSecurityConfigurers != null && !webSecurityConfigurers.isEmpty();
        if(!hasConfigurers) {
            throw new IllegalStateException("At least one non-null instance of "+ WebSecurityConfigurer.class.getSimpleName()+" must be exposed as a @Bean when using @EnableWebSecurity. Hint try extending "+ WebSecurityConfigurerAdapter.class.getSimpleName());
        }
        return webSecurity.build();
    }

Here is the relevant code which is causing trouble. This piece of code is from Spring Security. Spring Security is exposing a Filter for internal usage. However Spring Boot is catching it and trying to register as a Servlet Api Filter.

@dsyer
Copy link
Member

dsyer commented Dec 17, 2014

It's not really "for internal use" though is it? It's for your application security. If you don't want it, why is Spring Security on the classpath?

@dsyer
Copy link
Member

dsyer commented Dec 17, 2014

And there's nothing to stop you creating a FilterRegistrationBean and injecting that Filter if you need to change its behaviour. You know its type and bean id.

@cemo
Copy link
Contributor Author

cemo commented Dec 18, 2014

I have not noticed that you had already added seen beans check. This is disabling existing filters as I stated in issue description.

But there is still a problem. Spring's DelegatingFilterProxy is binding filter at init phase of filter. Your check is invalid for this situation. Maybe checking filter name is an option? (By the way Servlet Api is already checking filter names. In case a duplicated already configured same name filter, It is skipping it. )

@dsyer
Copy link
Member

dsyer commented Dec 18, 2014

OK so we appear to have agreed that there is already a mechanism in place for disabling existing filters. Maybe we need more documentation?

I don't get the last point about the "init" phase (there is no "init" phase for a Filter in a spring boot app - just the normal spring lifecycle). If you have a specific problem you are trying to solve, maybe you can explain it better with a small sample project?

@cemo
Copy link
Contributor Author

cemo commented Dec 18, 2014

+1 for more documentation.

I meant org.springframework.web.filter.DelegatingFilterProxy#initFilterBean method.

I am migrating a legacy application, I have to use FilterRegistrationBean + DelegatingFilterProxy currently. In case of a direct usage of FilterRegistrationBean + Security Filter, I am having issues. This is probably related to our application but at least currently I do not have any problem.

I believe that disabling mechanism of a filter is not complete without DelegatingFilterProxy support. In order to support such kind of filters, checking filter name might be a good idea.

@wilkinsona
Copy link
Member

Someone on Stack Overflow with the same problem

@wilkinsona wilkinsona added type: documentation A documentation update and removed type: enhancement A general enhancement labels Feb 11, 2015
@cemo
Copy link
Contributor Author

cemo commented Feb 12, 2015

What about DelegatingFilterProxy filters?

@dsyer
Copy link
Member

dsyer commented Feb 12, 2015

What about them? They are just filters.

@cemo
Copy link
Contributor Author

cemo commented Feb 12, 2015

Could you show me an example how would you disable a DelegatingFilterProxy filter please?

@dsyer
Copy link
Member

dsyer commented Feb 12, 2015

It's the same as the example Andy added to the docs:

@Bean
public FilterRegistrationBean registration() {
        DelegatingFilterProxy filter = filter(); // or inject it with a @Qualifier
    FilterRegistrationBean registration = new FilterRegistrationBean(filter);
    registration.setEnabled(false);
    return registration;
}

@hohwille
Copy link

Works like a charm. I had a custom solution that I used prior to this,
for those who do not like too much spring magic you can also use it:

public interface HttpServletFilter {
  void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
      throws IOException, ServletException;
}
public class HttpServletFilterWrapper implements Filter {
  private final HttpServletFilter filter;

  public HttpServletFilterWrapper(HttpServletFilter filter) {
    super();
    this.filter = filter;
  }

  @Override
  public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
      throws IOException, ServletException {
    this.filter.doFilter((HttpServletRequest) request, (HttpServletResponse) response, chain);
  }

  @Override
  public void init(FilterConfig filterConfig) throws ServletException {  }

  @Override
  public void destroy() {  }
}

Then in your @nAmed annotated bean implement HttpServletFilter instead of Filter and in your spring security config do something like this:

  @Inject
  private MyFilter myFilter; // your filter that implements HttpServletFilter

  @Override
  protected void configure(HttpSecurity http) throws Exception {
    http.foo().bar() //
        and().addFilterAfter(new HttpServletFilterWrapper(this.myFilter), LogoutFilter.class);
  }

@chris922
Copy link

Topic is quite old but was still relevant for us. I think we had the same issue @cemo described, the securityFilterChain was executed twice.

I first saw this in the logs

08:51:54.325 [http-nio-8080-exec-1] DEBUG org.springframework.security.web.FilterChainProxy  - /xyz at position 9 of 9 in additional filter chain; firing Filter: 'FilterSecurityInterceptor'
08:51:54.326 [http-nio-8080-exec-1] DEBUG org.springframework.security.web.access.intercept.FilterSecurityInterceptor  - Public object - authentication not attempted
08:51:54.327 [http-nio-8080-exec-1] DEBUG org.springframework.security.web.FilterChainProxy  - /xyz reached end of additional filter chain; proceeding with original chain
08:51:54.327 [http-nio-8080-exec-1] DEBUG org.springframework.security.web.FilterChainProxy  - /xyz at position 1 of 9 in additional filter chain; firing Filter: 'SecurityContextPersistenceFilter`

And then also in the startup-phase

09:05:03.204 [localhost-startStop-1] INFO  org.springframework.boot.web.servlet.DelegatingFilterProxyRegistrationBean  - Mapping filter: 'springSecurityFilterChain' to: [/*]
09:05:10.040 [localhost-startStop-1] INFO  org.springframework.boot.web.servlet.FilterRegistrationBean  - Mapping filter: 'org.springframework.security.filterChainProxy' to: [/*]

Thanks for the information here to find the solution.

For the sake of completeness here the solution via XML configuration

<bean class="org.springframework.boot.web.servlet.FilterRegistrationBean">
    <property name="filter" ref="springSecurityFilterChain" />
    <property name="enabled" value="false" />
</bean>

I debugged a lot to find this and also tried various configuration and found out that this issue happens when a <security:http> configuration is there without auto-config enabled (so when there is just one entry inside this tag.
Therefore I think this is a bigger issue when using Spring Boot and Spring Security and should be mentioned inside the Spring Security documentation explicitly.

@Ziemowit
Copy link

Ziemowit commented Nov 22, 2016

@chris922 Your solution helped me! Yeah! I have spent whole day to figure out what is happening :(

I had similar problem => moving legacy spring application to Spring Boot with security configuration defined in xml file.

Because of some reason 2 spring security filter chains were created by Spring Boot and my custom filters were called twice per each request.

(Debug view on application filters chain):

...
ApplicationFilterConfig[name=springSecurityFilterChain, filterClass=org.springframework.boot.web.servlet.DelegatingFilterProxyRegistrationBean$1]
ApplicationFilterConfig[name=org.springframework.security.filterChainProxy, filterClass=org.springframework.security.web.FilterChainProxy]
...

After applying your solution my application works well! Now the question is if it is some bug in my application that 2 spring security filter chains are created or it is some problem between xml config <=> Spring Boot?

Now I am only thinking loudly... Maybe it is becuase:

a) Spring Security documentation claims:

If you use an element within your application, a FilterChainProxy bean named "springSecurityFilterChain" is created and the configuration within the element is used to build a filter chain within FilterChainProxy

So as we have spring security configuration in xml we have tag defined there. So "springSecurityFilterChain" is created.

b) As Spring Boot find Spring-Security on classpath it creates the 2nd one security chain during auto-configuration.

As a result of points a) and b) we have 2 security filter chains in application.

@lxnx0
Copy link

lxnx0 commented Dec 9, 2016

I'm having the same problem today.

For unknown reason, I keep on getting NullPointerException without any clue. Until I found out that the Security Filter was ran twice.

Following is my solution :

@Bean
public FilterRegistrationBean filterRegistrationBean() throws Exception {
    FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
    filterRegistrationBean.setEnabled(false);
    filterRegistrationBean.setFilter(authenticationTokenFilterBean());
    return filterRegistrationBean;
}

@lopiter
Copy link

lopiter commented Jan 16, 2017

is this issue is closed?? i got same problem..

see org.springframework.boot.web.servlet.ServletContextInitializerBeans class source..

private void addAdaptableBeans(ListableBeanFactory beanFactory) {
        MultipartConfigElement multipartConfig = this.getMultipartConfig(beanFactory);
        this.addAsRegistrationBean(beanFactory, Servlet.class, new ServletContextInitializerBeans.ServletRegistrationBeanAdapter(multipartConfig));
        this.addAsRegistrationBean(beanFactory, Filter.class, new ServletContextInitializerBeans.FilterRegistrationBeanAdapter(null));
        Iterator var3 = ServletListenerRegistrationBean.getSupportedTypes().iterator();

        while(var3.hasNext()) {
            Class listenerType = (Class)var3.next();
            this.addAsRegistrationBean(beanFactory, EventListener.class, listenerType, new ServletContextInitializerBeans.ServletListenerRegistrationBeanAdapter(null));
        }

    }

bean class that implements javax.servlet.Filter interface is registered to filter automatically

@wilkinsona
Copy link
Member

wilkinsona commented Jan 16, 2017

is this issue is closed?

Yes. It says so at the top of the page

bean class that implements javax.servlet.Filter interface is registered to filter automatically

That's by design. Please read the documentation to learn how to disable that registration.

@HappySnoopy
Copy link

Sorry to bother everyone, but the FilterRegistrationBean did not work for me.

My configuration is :

    @Bean
    public FilterRegistrationBean afterAuthenticatedProcessingFilter(
            @Autowired RedisTool<String, String> redisTool,
            @Autowired CaptchaSettingServiceImpl captchaSettingService,
            @Autowired SystemSettingHelper systemSettingHelper) {
        FilterRegistrationBean registration = new FilterRegistrationBean();

        AfterAuthenticatedProcessingFilter filter = new AfterAuthenticatedProcessingFilter();
        filter.setRedisTool(redisTool);
        filter.setCaptchaSettingService(captchaSettingService);
        filter.setSystemSettingHelper(systemSettingHelper);
        registration.setFilter(filter);
        registration.setName("afterAuthenticatedProcessingFilter");
        registration.setEnabled(false);
        return registration;
    }

and the AfterAuthenticatedProcessingFilter is:

public class AfterAuthenticatedProcessingFilter extends GenericFilterBean {
    // ignore the implement codes
}

but when my application bootstap, an exception was throwed and the root cause is:

Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'org.springframework.security.web.DefaultSecurityFilterChain#4': Unsatisfied dependency expressed through constructor parameter 1: Could not convert argument value of type [java.util.ArrayList] to required type [java.util.List]: Failed to convert value of type 'java.util.ArrayList' to required type 'java.util.List'; nested exception is java.lang.IllegalStateException: Cannot convert value of type 'org.springframework.boot.web.servlet.FilterRegistrationBean' to required type 'javax.servlet.Filter': no matching editors or conversion strategy found
at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:723) ~[spring-beans-4.3.10.RELEASE.jar:4.3.10.RELEASE]
at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:189) ~[spring-beans-4.3.10.RELEASE.jar:4.3.10.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1193) ~[spring-beans-4.3.10.RELEASE.jar:4.3.10.RELEASE]
some other logs

so, have anyone seen a similar problem? and could anyone give me some sugguestion? thanks a lot!

@snicoll
Copy link
Member

snicoll commented Dec 15, 2017

@winters1224 please ask questions on stackoverflow.

@HappySnoopy
Copy link

@snicoll OK,thanks.

@mbhave
Copy link
Contributor

mbhave commented Apr 8, 2019

@OrangeDog This issue is closed. If you have an enhancement request, please create a new issue.

@neshtaMedia
Copy link

When migrating from legacy code (with a lot of XML configuration), it would be great to be able to disable automatic registration of filters and servlets. This is exactly what I'm doing now and I have to disable a lot of filters with FilterRegistrationBean.setEnabled(false)

@wilkinsona
Copy link
Member

@neshtaMedia I believe you could do that with a custom subclass of org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext that overrides getServletContextInitializerBeans().

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type: documentation A documentation update
Projects
None yet
Development

No branches or pull requests