Search code examples
javaspringspring-securitymultipart

How to use an AbstractSecurityWebApplicationInitial?


I'm new to Spring and I have a problem with MultipartFilter and CSRF. I found in spring.io security docs that you can use the following class to put MultipartFilter before the security filter so MultiparFilter don't get mess. My problem is that although I have the bellow class, it appears to not be loaded, for example the log message doesn't appear.

Should I put AbstractSecurityWebApplicationInitializer subclass in some specific place? Do I need to add other configurations or tags to use it?

package com.penalara.ghcserviceapi.config;

import javax.servlet.ServletContext;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer;
import org.springframework.web.multipart.support.MultipartFilter;

public class SecurityApplicationInitializer extends AbstractSecurityWebApplicationInitializer {

    public final static Log log = LogFactory.getLog(SecurityApplicationInitializer.class);

    @Override
    protected void beforeSpringSecurityFilterChain(ServletContext servletContext) {

        insertFilters(servletContext, new MultipartFilter());

        log.debug("Inicializados los filtros.");

    }

}

I've also tried to put a @Configuration annotation but with the same (bad) result (I think because there isn't any bean).


As you can see bellow I'm configuring it through Java instead of xml and I prefer to stick to it if possible.

This is my security configuration:

package com.penalara.ghcserviceapi.config;

import javax.servlet.ServletContext;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.embedded.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.authentication.configurers.GlobalAuthenticationConfigurerAdapter;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.web.filter.DelegatingFilterProxy;
import org.springframework.web.multipart.support.MultipartFilter;

import com.penalara.ghcserviceapi.model.security.CuentaUser;
import com.penalara.ghcserviceapi.repositories.CuentaRepository;

@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    private static final Log log = LogFactory.getLog(SecurityConfig.class);

    @Autowired
    CuentaRepository accountRepository;

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {

        auth.userDetailsService(userDetailsService());

    }

    @Bean
    public UserDetailsService userDetailsService() {
        return (username) -> accountRepository
                .findByUsuario(username)
                .map(a -> new CuentaUser(a, AuthorityUtils.createAuthorityList("USER", "write")))
                .orElseThrow(
                        () -> new UsernameNotFoundException("could not find the user '"
                                + username + "'"));
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {

        http
            .authorizeRequests()
                .anyRequest().authenticated()
                .and()
            .httpBasic();

    }

}

I have also tried with http.addFilterBefore but it still throw the CSRF error.

@Override
    protected void configure(HttpSecurity http) throws Exception {

        MultipartFilter mf = new MultipartFilter();
        mf.setServletContext(context);
        http.addFilterBefore(mf, SecurityContextPersistenceFilter.class);

        http.authorizeRequests().anyRequest().authenticated().and().httpBasic();
    }

Note: I have tried with SecurityContextPersistenceFilter, LogoutFilter, and others but with the same problem. And using CorsFilter.class, but throw a exception Error creating bean with name 'springSecurityFilterChain' so I think you can't add it before with this method.

This is the stacktrace when choosing add before CorsFilter:

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'springSecurityFilterChain' defined in class path resource [org/springframework/security/config/annotation/web/configuration/WebSecurityConfiguration.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [javax.servlet.Filter]: Factory method 'springSecurityFilterChain' threw exception; nested exception is java.lang.IllegalArgumentException: Cannot register after unregistered Filter class org.springframework.web.filter.CorsFilter
    at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:599) ~[spring-beans-4.2.5.RELEASE.jar:4.2.5.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1123) ~[spring-beans-4.2.5.RELEASE.jar:4.2.5.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1018) ~[spring-beans-4.2.5.RELEASE.jar:4.2.5.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:510) ~[spring-beans-4.2.5.RELEASE.jar:4.2.5.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:482) ~[spring-beans-4.2.5.RELEASE.jar:4.2.5.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306) ~[spring-beans-4.2.5.RELEASE.jar:4.2.5.RELEASE]
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230) ~[spring-beans-4.2.5.RELEASE.jar:4.2.5.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302) ~[spring-beans-4.2.5.RELEASE.jar:4.2.5.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202) ~[spring-beans-4.2.5.RELEASE.jar:4.2.5.RELEASE]
    at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1060) ~[spring-context-4.2.5.RELEASE.jar:4.2.5.RELEASE]
    at org.springframework.web.filter.DelegatingFilterProxy.initDelegate(DelegatingFilterProxy.java:326) ~[spring-web-4.2.5.RELEASE.jar:4.2.5.RELEASE]
    at org.springframework.web.filter.DelegatingFilterProxy.initFilterBean(DelegatingFilterProxy.java:235) ~[spring-web-4.2.5.RELEASE.jar:4.2.5.RELEASE]
    at org.springframework.web.filter.GenericFilterBean.init(GenericFilterBean.java:199) ~[spring-web-4.2.5.RELEASE.jar:4.2.5.RELEASE]
    at org.apache.catalina.core.ApplicationFilterConfig.initFilter(ApplicationFilterConfig.java:279) ~[tomcat-embed-core-8.0.32.jar:8.0.32]
    at org.apache.catalina.core.ApplicationFilterConfig.<init>(ApplicationFilterConfig.java:109) ~[tomcat-embed-core-8.0.32.jar:8.0.32]
    at org.apache.catalina.core.StandardContext.filterStart(StandardContext.java:4659) [tomcat-embed-core-8.0.32.jar:8.0.32]
    at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5281) [tomcat-embed-core-8.0.32.jar:8.0.32]
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:147) [tomcat-embed-core-8.0.32.jar:8.0.32]
    at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1408) [tomcat-embed-core-8.0.32.jar:8.0.32]
    at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1398) [tomcat-embed-core-8.0.32.jar:8.0.32]
    at java.util.concurrent.FutureTask.run(FutureTask.java:266) [na:1.8.0_91]
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) [na:1.8.0_91]
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) [na:1.8.0_91]
    at java.lang.Thread.run(Thread.java:745) [na:1.8.0_91]
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [javax.servlet.Filter]: Factory method 'springSecurityFilterChain' threw exception; nested exception is java.lang.IllegalArgumentException: Cannot register after unregistered Filter class org.springframework.web.filter.CorsFilter
    at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:189) ~[spring-beans-4.2.5.RELEASE.jar:4.2.5.RELEASE]
    at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:588) ~[spring-beans-4.2.5.RELEASE.jar:4.2.5.RELEASE]
    ... 23 common frames omitted
Caused by: java.lang.IllegalArgumentException: Cannot register after unregistered Filter class org.springframework.web.filter.CorsFilter
    at org.springframework.security.config.annotation.web.builders.FilterComparator.registerBefore(FilterComparator.java:163) ~[spring-security-config-4.0.3.RELEASE.jar:4.0.3.RELEASE]
    at org.springframework.security.config.annotation.web.builders.HttpSecurity.addFilterBefore(HttpSecurity.java:977) ~[spring-security-config-4.0.3.RELEASE.jar:4.0.3.RELEASE]
    at com.penalara.ghcserviceapi.config.SecurityConfig.configure(SecurityConfig.java:60) ~[classes/:na]
    at org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter.getHttp(WebSecurityConfigurerAdapter.java:199) ~[spring-security-config-4.0.3.RELEASE.jar:4.0.3.RELEASE]
    at org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter.init(WebSecurityConfigurerAdapter.java:290) ~[spring-security-config-4.0.3.RELEASE.jar:4.0.3.RELEASE]
    at org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter.init(WebSecurityConfigurerAdapter.java:67) ~[spring-security-config-4.0.3.RELEASE.jar:4.0.3.RELEASE]
    at com.penalara.ghcserviceapi.config.SecurityConfig$$EnhancerBySpringCGLIB$$978ef7.init(<generated>) ~[classes/:na]
    at org.springframework.security.config.annotation.AbstractConfiguredSecurityBuilder.init(AbstractConfiguredSecurityBuilder.java:370) ~[spring-security-config-4.0.3.RELEASE.jar:4.0.3.RELEASE]
    at org.springframework.security.config.annotation.AbstractConfiguredSecurityBuilder.doBuild(AbstractConfiguredSecurityBuilder.java:324) ~[spring-security-config-4.0.3.RELEASE.jar:4.0.3.RELEASE]
    at org.springframework.security.config.annotation.AbstractSecurityBuilder.build(AbstractSecurityBuilder.java:41) ~[spring-security-config-4.0.3.RELEASE.jar:4.0.3.RELEASE]
    at org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration.springSecurityFilterChain(WebSecurityConfiguration.java:105) ~[spring-security-config-4.0.3.RELEASE.jar:4.0.3.RELEASE]
    at org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration$$EnhancerBySpringCGLIB$$cdd84cf3.CGLIB$springSecurityFilterChain$2(<generated>) ~[spring-security-config-4.0.3.RELEASE.jar:4.0.3.RELEASE]
    at org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration$$EnhancerBySpringCGLIB$$cdd84cf3$$FastClassBySpringCGLIB$$527e2d9c.invoke(<generated>) ~[spring-security-config-4.0.3.RELEASE.jar:4.0.3.RELEASE]
    at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:228) ~[spring-core-4.2.5.RELEASE.jar:4.2.5.RELEASE]
    at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:355) ~[spring-context-4.2.5.RELEASE.jar:4.2.5.RELEASE]
    at org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration$$EnhancerBySpringCGLIB$$cdd84cf3.springSecurityFilterChain(<generated>) ~[spring-security-config-4.0.3.RELEASE.jar:4.0.3.RELEASE]
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_91]
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_91]
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_91]
    at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_91]
    at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:162) ~[spring-beans-4.2.5.RELEASE.jar:4.2.5.RELEASE]
    ... 24 common frames omitted

Solution

  • Well, after learning more about CSRF and Spring I found that there isn't any problem really. There are some of my conclusions.

    • As I wasn't deploying a servelet (war) and because of this the AbstractSecurityWebApplicationInitializer wasn't loaded.
    • When I used MultipartFilter before CSRF filter it really get added and loaded as show by the logs Creating filter chain: Ant [pattern='/**'], [..., org.springframework.web.multipart.support.MultipartFilter@1357a33a, org.springframework.security.web.csrf.CsrfFilter@695fc2fc, ...].
    • And the big problem is that I supposed that there isn't any need to include a csrf parameter because it had been included automatically. It can recognise CSRF token in multipart, if you include it!

    Now I use CsrfTokenResponseHeaderBindingFilter, like it is suggested in Spring Security’s CSRF protection for REST services: the client side and the server side to include the CSRF in the header (that is useful for a REST API). You could use other solutions like in With Spring Security 3.2.0.RELEASE, how can I get the CSRF token in a page that is purely HTML with no tag libs.

    And for the multipartfilter problem, you can read other questions like Spring Security 3.2 CSRF support for multipart requests.

    To sum up, the question is a little dumb from a nob in Spring, but here is if another nob want to read it.