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
Well, after learning more about CSRF and Spring I found that there isn't any problem really. There are some of my conclusions.
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, ...]
.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.