question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

`NoSuchBeanDefinitionException` after migration from `antMatchers` to `requestMatchers` (5.8.0)

See original GitHub issue

Hi,

I upgraded from 5.7.x to 5.8.0 and replaced the deprecated antMatchers(String...) with requestMatchers(String...) as suggested by the Javadoc, but now i’m getting the following error on application startup (note the weird exception message “No bean named 'A Bean named…”):

org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named '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.' available
    at org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry.createMvcMatchers(AbstractRequestMatcherRegistry.java:187)
    at org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry.requestMatchers(AbstractRequestMatcherRegistry.java:302)
    at org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry.requestMatchers(AbstractRequestMatcherRegistry.java:329)
    at com.example.spring.security.SecurityConfig.filterChain(SecurityConfig.java:36)
    at com.example.spring.security.SecurityConfig$$EnhancerBySpringCGLIB$$d314e6bc.CGLIB$filterChain$0(<generated>)
    at com.example.spring.security.SecurityConfig$$EnhancerBySpringCGLIB$$d314e6bc$$FastClassBySpringCGLIB$$f1e371fd.invoke(<generated>)
    at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:244)
    at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:331)
    at com.example.spring.security.SecurityConfig$$EnhancerBySpringCGLIB$$d314e6bc.filterChain(<generated>)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:566)
    at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:154)

SpringWebAppInitializer.java

public class SpringWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {

    @Override
    protected Class<?>[] getRootConfigClasses() {
        return new Class<?>[] { RootConfig.class, SecurityConfig.class };
    }

    @Override
    protected Class<?>[] getServletConfigClasses() {
        return new Class<?>[] { ServletConfig.class };
    }

    @Override
    protected String[] getServletMappings() {
        return new String[] { "/" };
    }

    public static class SecurityWebAppInitializer extends AbstractSecurityWebApplicationInitializer {}
}

RootConfig.java

@Configuration
@ComponentScan
public class RootConfig {}

SecurityConfig.java

@Configuration
@ComponentScan
@EnableWebSecurity
@EnableMethodSecurity(securedEnabled = true)
public class SecurityConfig implements ApplicationContextAware {

    private ApplicationContext context;

    @Override
    public void setApplicationContext(ApplicationContext context) {
        this.context = context;
    }

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http.jee().authenticatedUserDetailsService(token -> context.getBean(UserDetailsService.class).loadUserDetails(token)).mappableAuthorities(UserRole.stream().map(UserRole::getName).collect(Collectors.toSet())) // lambda required for warm context refresh
            .and().authorizeHttpRequests().requestMatchers("/admin/**").hasAuthority(RoleNames.ROLE_ADMIN) // this doesn't work but antMatchers did 
            .and().csrf().disable().logout().logoutSuccessHandler(new HttpStatusReturningLogoutSuccessHandler(HttpStatus.NO_CONTENT));
        return http.build();
    }
}

ServletConfig.java

@Configuration
@ComponentScan
@EnableWebMvc
@EnableAspectJAutoProxy
public class ServletConfig implements WebMvcConfigurer {}

I noted that if I return null from getRootConfigClasses() and move both RootConfig.class & SecurityConfig.class to the array returned by getServletConfigClasses(), the application starts, but I don’t understand the reason behind this, and I would like to keep separated the beans of root and servlet contexts.

Note also that moving only SecurityConfig.class from the root config to the servlet config produces another exception on application startup:

org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'springSecurityFilterChain' available
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanDefinition(DefaultListableBeanFactory.java:874)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getMergedLocalBeanDefinition(AbstractBeanFactory.java:1358)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:309)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:213)
    at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1160)
    at org.springframework.web.filter.DelegatingFilterProxy.initDelegate(DelegatingFilterProxy.java:334)
    at org.springframework.web.filter.DelegatingFilterProxy.initFilterBean(DelegatingFilterProxy.java:239)
    at org.springframework.web.filter.GenericFilterBean.init(GenericFilterBean.java:239)
    at org.apache.catalina.core.ApplicationFilterConfig.initFilter(ApplicationFilterConfig.java:272)
    at org.apache.catalina.core.ApplicationFilterConfig.<init>(ApplicationFilterConfig.java:106)
    at org.apache.catalina.core.StandardContext.filterStart(StandardContext.java:4609)
    at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5248)
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:183)
    at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1393)
    at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1383)
    at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
    at org.apache.tomcat.util.threads.InlineExecutorService.execute(InlineExecutorService.java:75)
    at java.base/java.util.concurrent.AbstractExecutorService.submit(AbstractExecutorService.java:140)
    at org.apache.catalina.core.ContainerBase.startInternal(ContainerBase.java:916)
    at org.apache.catalina.core.StandardHost.startInternal(StandardHost.java:835)
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:183)
    at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1393)
    at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1383)
    at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
    at org.apache.tomcat.util.threads.InlineExecutorService.execute(InlineExecutorService.java:75)
    at java.base/java.util.concurrent.AbstractExecutorService.submit(AbstractExecutorService.java:140)
    at org.apache.catalina.core.ContainerBase.startInternal(ContainerBase.java:916)
    at org.apache.catalina.core.StandardEngine.startInternal(StandardEngine.java:265)
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:183)
    at org.apache.catalina.core.StandardService.startInternal(StandardService.java:430)
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:183)
    at org.apache.catalina.core.StandardServer.startInternal(StandardServer.java:930)
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:183)
    at org.apache.catalina.startup.Catalina.start(Catalina.java:772)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:566)
    at org.apache.catalina.startup.Bootstrap.start(Bootstrap.java:345)
    at org.apache.catalina.startup.Bootstrap.main(Bootstrap.java:476)

To Reproduce, please download this sample app and deploy it into Tomcat 9.0.

Thanks.

Issue Analytics

  • State:closed
  • Created 10 months ago
  • Comments:9 (5 by maintainers)

github_iconTop GitHub Comments

1reaction
marcusdacoregiocommented, Dec 5, 2022

Hi, @albertus82. After reaching out to the team and getting some good explanations, especially from @rwinch and @dsyer:

The getRootConfigClasses and getServletConfigClasses are meant to be used by applications with more than one Servlet that depends on a shared set of service beans, in such cases, it could make sense to have shared services in the root context so they are only created once for all servlet instances. This setup was common a long time ago (XML days) but not common now, even with WAR deployments.

Therefore, this was before the concept of MvcRequestMatcher, as I mentioned, the documentation states that you need the Spring Security and Spring MVC beans to be visible to each other. If you have multiple Servlets it then becomes complicated because you run into a situation where you likely need a springSecurityFilterChain for each servlet so that you can ensure the routing of Security and the servlets align. There are better ways to handle things than having more than one servlet (DispatcherServlet is meant to be the entry point to everything and it dispatches out to various controllers rather than having lots of DispatcherServlets).

With that said, if such a setup makes sense for you, you can configure your initializer like this:

@Override
protected Class<?>[] getRootConfigClasses() {
	return new Class<?>[] { RootConfig.class };
}

@Override
protected Class<?>[] getServletConfigClasses() {
	return new Class<?>[] { ServletConfig.class, SecurityConfig.class };
}

And override AbstractSecurityWebApplicationInitializer.getDispatcherWebApplicationContextSuffix() to return AbstractDispatcherServletInitializer.DEFAULT_SERVLET_NAME, so that Spring Security is loaded from the DispatcherServlet’s ApplicationContext.

The final code for your initializer would be:

public class SpringWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {

	@Override
	protected Class<?>[] getRootConfigClasses() {
		return new Class<?>[] { RootConfig.class };
	}

	@Override
	protected Class<?>[] getServletConfigClasses() {
		return new Class<?>[] { ServletConfig.class, SecurityConfig.class };
	}

	@Override
	protected String[] getServletMappings() {
		return new String[] { "/" };
	}

	public static class SecurityWebAppInitializer extends AbstractSecurityWebApplicationInitializer {

		@Override
		protected String getDispatcherWebApplicationContextSuffix() {
			return AbstractDispatcherServletInitializer.DEFAULT_SERVLET_NAME;
		}

	}

}

I hope that the explanation makes sense to you.

1reaction
marcusdacoregiocommented, Dec 1, 2022

I tried but with no luck.

Something might be different then, with that setup I get Authenticated user: ADMIN - Roles: [ROLE_ADMIN] in the root path.

Maybe we could think about this logic?

That was my first discussion with @rwinch when designing this feature. It is designed this way because if you have Spring WebMVC in the classpath but somehow don’t have the mvcHandlerMappingIntrospector bean, that means that Spring Security Configuration is not in the same ApplicationContext as your DispatcherServlet, and the MvcRequestMatcher needs that bean, see here. If you do not want to use MvcRequestMatcher, an explicit configuration is needed.

That said, I brought this matter to the team’s attention and we are looking into it.

Read more comments on GitHub >

github_iconTop Results From Across the Web

NoSuchBeanDefinitionException after migrate from spring ...
Today I want to upgrade from spring boot 1.5.7 to 2.0.1 but at runtime spring cannot find any of my spring-beans that are...
Read more >
spring-projects/spring-security - Gitter
The problem is caused by a NoSuchBeanDefinitionException while trying to use nettyContext bean which is declared on each respective context configuration for ...
Read more >
Spring Security without the WebSecurityConfigurerAdapter
In Spring Security 5.4 we introduced the ability to configure HttpSecurity by creating a SecurityFilterChain bean. Below is an example ...
Read more >
Add 'securityMatcher' as an alias of 'requestMatcher' #9159
When configuring security rules, it will be very helpful to understand what we are configuring. Example with this kind of configuration ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found