2

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
PhoneixS
  • 10,574
  • 6
  • 57
  • 73
  • Have you tried adding .addFilterBefore ? Not sure how it should work with MultipartFilter, but with CORS filter you can use something like `http.addFilterBefore(new CorsFilter(source), LogoutFilter.class)` It's in SecurityConfig – lenach87 May 19 '16 at 16:01
  • I've tried but with the same result. See question edit. – PhoneixS May 19 '16 at 16:31
  • Can you post the stack trace of your error? And is the problem the same if your do `http.csrf().disable()` ? – lenach87 May 19 '16 at 17:31
  • No, if I disable csrf the CSRF problem is gone, but I don't want to disable it. I'll post the stack trace. – PhoneixS May 20 '16 at 07:08
  • I seem confused - you have problems with 2 filters - CSRF and MultipartFilter, right? Cors is another thing, I just gave an example of custom filter. In your case, if you only have problem with CSRF but can implement MultipartFilter in `addFilterBefore`, maybe you can try adding `.addFilterAfter(new CsrfHeaderFilter(), CsrfFilter.class)` and copy implementation of CsrfHeaderFilter class from this article https://spring.io/blog/2015/01/12/the-login-page-angular-js-and-spring-security-part-ii (see paragraph CSRF Protection) – lenach87 May 20 '16 at 09:08
  • The problem is you can't use Multipart with CSRF protection by default (as explained in http://docs.spring.io/spring-security/site/docs/current/reference/html/csrf.html#csrf-multipart because you need to put Multipart before security chain. And if you read the question (or the documentation) they give a solution but appears to not get executed (which it's what/why I asked here). I will try with your option. – PhoneixS May 20 '16 at 09:30
  • But yes, I miss the name, what I really want to do is put it before CsrfFilter. And I tried with it, but it doesn't fix the "Expected CSRF token not found. Has your session expired?" problem with multipart. – PhoneixS May 20 '16 at 09:32
  • Now I understand what you problem is. Hope you find the answer! – lenach87 May 20 '16 at 12:05
  • @lenach87 Finally I found that I'm a little nob but thank you for your time. – PhoneixS May 24 '16 at 09:53

1 Answers1

0

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.

Community
  • 1
  • 1
PhoneixS
  • 10,574
  • 6
  • 57
  • 73
  • Did you add the filter configuration as XML as described in that link? It'd be interesting to see if it can be done with annotations in Spring Boot, which causes tomcat to ignore the `SecurityApplicationInitializer`. – Adam Feb 22 '18 at 12:03
  • @Adam I used java config as said in the link of CsrfTokenResponseHeaderBindingFilter. – PhoneixS Feb 22 '18 at 16:19
  • Ah OK. Like this: https://docs.spring.io/spring-security/site/docs/current/reference/html/jc.html#jc-httpsecurity – Adam Feb 23 '18 at 12:42