After a minor upgrade of my Spring framework from 4.3.13 to 4.3.14 and spring-security 4.2.3 to 4.2.4, my web application fails to initialize (Tomcat 8.5.23, JDK 1.8.0_152), with the following error:
[DEBUG] Retrieved dependent beans for bean 'springSecurityFilterChain': [webSecurityExpressionHandler]
[WARN] Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException:
Error creating bean with name 'springSecurityFilterChain' defined in org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration:
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
org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'mvcHandlerMappingIntrospector' available: A Bean named mvcHandlerMappingIntrospector of type org.springframework.web.servlet.handler.HandlerMappingIntrospector is required to use MvcRequestMatcher.
Please ensure Spring Security & Spring MVC are configured in a shared ApplicationContext.
The class that throws this exception is CorsConfigurer.MvcCorsFilter, which is part of spring-security
.
I found a useful thread on this error (SPR-16301) that explain that this exception is the result of additional checking for invalid configurations in the new versions of Spring (5.0.3 and 4.3.14, which were released on the same day).
My WebConfig
class has the @EnableWebSecurity
annotation, which, as noted in the above thread, imports DelegatingWebMvcConfiguration
, which declares a HandlerMappingIntrospector.
Essentially, the thread suggests that more than one context has been declared (the root context and a child context), and that I may have enabled Spring Security in multiple contexts.
However, I am not seeing the solution in my case. Do I have duplicate/overlapping annotations? Have I loaded the Root and Security configs incorrectly in my WebInitializer
class?
My Spring configuration classes are listed below (except for my AuthenticationProvider
).
WebInitializer.java
package com.x.spring;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
public final class WebInitializer extends AbstractAnnotationConfigDispatcherServletInitializer
{
@Override
protected Class<?>[] getRootConfigClasses()
{
return new Class<?>[] {RootConfig.class, WebSecurityConfig.class};
}
@Override
protected Class<?>[] getServletConfigClasses()
{
return new Class<?>[] {WebConfig.class};
}
@Override
protected String[] getServletMappings()
{
return new String[] {"/"};
}
@Override
protected DispatcherServlet createDispatcherServlet(final WebApplicationContext servletAppContext)
{
final DispatcherServlet dispatcherServlet = (DispatcherServlet) super.createDispatcherServlet(servletAppContext);
dispatcherServlet.setThrowExceptionIfNoHandlerFound(true);
return dispatcherServlet;
}
}
RootConfig.java
package com.x.spring;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.ComponentScan.Filter;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
@Configuration
@ComponentScan(basePackages = {"com.x.spring.controller"}, excludeFilters = {@Filter(type = FilterType.ANNOTATION, value = EnableWebMvc.class)})
public class RootConfig
{
}
WebConfig.java
package com.x.spring;
import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.ResourceBundleMessageSource;
import org.springframework.web.multipart.commons.CommonsMultipartResolver;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.PathMatchConfigurer;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.view.tiles3.TilesConfigurer;
import org.springframework.web.servlet.view.tiles3.TilesViewResolver;
@Configuration
@EnableWebMvc
@ComponentScan("com.x.spring.controller")
public class WebConfig extends WebMvcConfigurerAdapter
{
@Bean
public ViewResolver viewResolver()
{
final TilesViewResolver resolver = new TilesViewResolver();
return resolver;
}
@Bean
public TilesConfigurer tilesConfigurer()
{
final TilesConfigurer tiles = new TilesConfigurer();
tiles.setDefinitions(new String[] {"/WEB-INF/tiles.xml"});
tiles.setCheckRefresh(true);
return tiles;
}
@Bean
public CommonsMultipartResolver multipartResolver()
{
final CommonsMultipartResolver resolver = new CommonsMultipartResolver();
resolver.setMaxUploadSize(10000000);
return resolver;
}
// These methods enable '.' in RequestMappings.
@Override
public final void configurePathMatch(final PathMatchConfigurer configurer)
{
configurer.setUseRegisteredSuffixPatternMatch(true);
return;
}
@Override
public final void addResourceHandlers(final ResourceHandlerRegistry registry)
{
registry.addResourceHandler("/css/**").addResourceLocations("/css/");
registry.addResourceHandler("/js/**").addResourceLocations("/js/");
registry.addResourceHandler("/images/**").addResourceLocations("/images/");
return;
}
@Override
public void addCorsMappings(final CorsRegistry registry)
{
registry.addMapping("/**");
return;
}
}
WebSecurityConfig.java
package com.x.spring;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import com.x.spring.security.XAuthenticationProvider;
/**
* Spring web security configuration.
* This class must be registered with WebInitializer.
*/
@Configuration
@EnableWebSecurity
@ComponentScan("com.x.spring.security")
public class WebSecurityConfig extends WebSecurityConfigurerAdapter
{
@Autowired
private XAuthenticationProvider authenticationProvider;
/**
* https://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/
*/
public WebSecurityConfig()
{
super();
return;
}
@Override
public void configure(final WebSecurity web) throws Exception
{
super.configure(web);
return;
}
@Override
protected void configure(final HttpSecurity http) throws Exception
{
http
.csrf().disable()
.cors()
.and().headers().frameOptions().sameOrigin()
.and()
.authorizeRequests()
.antMatchers("/my-account").hasAuthority("USER")
.anyRequest().permitAll()
.and().httpBasic().realmName("X")
.and().formLogin().loginPage("/login")
.and().logout().logoutSuccessUrl("/")
.and().rememberMe().key("XSecured")
;
return;
}
@Override
protected void configure(final AuthenticationManagerBuilder auth) throws Exception
{
auth.authenticationProvider(this.authenticationProvider);
return;
}
}
WebSecurityInitializer.java
package com.x.spring;
import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer;
/**
* This class is discovered by Spring and registers the DelegatingFilterProxy with the web container.
* It will intercept requests coming into the application and delegate them to a bean whose ID is springSecurityFilterChain.
*/
public final class WebSecurityInitializer extends AbstractSecurityWebApplicationInitializer
{
}
The initialization error was caused by a duplicate context. Specifically, the RootConfig
class was redundant with the WebConfig
class. This problem was solve by deleting RootConfig
and changing WebInitializer.getRootConfigClasses()
to return WebConfig
instead of RootConfig
and WebInitializer.getServletConfigClasses()
to return null
.
My working WebInitializer
class is below. Aside from deleting RootConfig
, no changes were needed to other classes.
WebInitializer.java
package com.x.spring;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
public final class WebInitializer extends AbstractAnnotationConfigDispatcherServletInitializer
{
@Override
protected Class<?>[] getRootConfigClasses()
{
return new Class<?>[] {WebConfig.class, WebSecurityConfig.class};
}
@Override
protected Class<?>[] getServletConfigClasses()
{
return null;
}
@Override
protected String[] getServletMappings()
{
return new String[] {"/"};
}
@Override
protected DispatcherServlet createDispatcherServlet(final WebApplicationContext servletAppContext)
{
final DispatcherServlet dispatcherServlet = (DispatcherServlet) super.createDispatcherServlet(servletAppContext);
dispatcherServlet.setThrowExceptionIfNoHandlerFound(true);
return dispatcherServlet;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With