Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring Security exception after minor version update: "Please ensure Spring Security & Spring MVC are configured in a shared ApplicationContext"

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
{
}
like image 763
vallismortis Avatar asked Sep 07 '25 02:09

vallismortis


1 Answers

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;
    }
like image 117
vallismortis Avatar answered Sep 10 '25 07:09

vallismortis