-
Notifications
You must be signed in to change notification settings - Fork 5.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
DispatcherServletDelegatingRequestMatcher causes errors when there is more than one ServletContext #14418
Comments
I have a very similar problem that is not just limited to running tests with MockMvc. It can also happen in a Spring Boot application at runtime, if all of the following conditions are given:
This will get you an internal server error with HTTP status code 500. With Spring Boot 3.1.0, you would see a 404 as expected for a non-existing endpoint. The stacktrace is similar to the one from the initial post, but the message is slightly different. In my case, the servlet it fails to find does have a non-empty name: The sample repository from the initial post seems unavailable. I have created another sample repository with a sample application to reproduce the issue I described: https://github.com/atrepczik/spring-security-request-matching-issue |
@LewisMcReu Could you perhaps add the fix you had in mind? I didn't manage to (yet) and the repository you mentioned is empty right now. Nevermind,... I just figured it out I suppose: static class FixMissingServletPathProcessor implements RequestPostProcessor {
@Override
public MockHttpServletRequest postProcessRequest(MockHttpServletRequest request) {
request.setHttpServletMapping(new HttpServletMapping() {
@Override
public String getMatchValue() {
return "";
}
@Override
public String getPattern() {
return "";
}
@Override
public String getServletName() {
return "dispatcherServlet";
}
@Override
public MappingMatch getMappingMatch() {
return MappingMatch.PATH;
}
});
request.setContextPath("/");
return request;
}
} |
Did anyone solve the issue. I am looking to solve this for a while. How do I get past this issue? |
I have a similar issue and spent the last 3 days trying to reproduce it and find the exact version of spring-boot / spring-security where it occured for the first time. Scenario:
Link to my example repo: https://github.com/seschi98/demo-spring-dispatcher-servlet-error With Spring Boot 3.1.1 (Security 6.1.1) everything works as expected. No errors on startup, With Spring Boot 3.1.2 (Security 6.1.2) the server won't start anymore. I could backtrack this problem to cf2c8da With Spring Boot 3.1.3 (Security 6.1.3) its the same issue, just with a better error message. See 0df1884 With Spring Boot 3.1.4 (Security 6.1.4), Spring Boot 3.1.5 (Security 6.1.5) and Spring Boot 3.1.6 (Security 6.1.5) nothing changed. With Spring Boot 3.1.7 (Security 6.1.6) the server starts again (yayyy), probably thanks to 624dcaf. Unfortunately when you call java.lang.IllegalArgumentException: Failed to find servlet [dispatcherServletRegistration] in the servlet context
at org.springframework.util.Assert.notNull(Assert.java:204) ~[spring-core-6.0.15.jar:6.0.15]
at org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry$DispatcherServletDelegatingRequestMatcher.matcher(AbstractRequestMatcherRegistry.java:544) ~[spring-security-config-6.1.6.jar:6.1.6] If I remove the line management.server.port: 8081 from my I hope this information helps understanding the issue and someone with better knowledge of the spring-security internals will be able to figure out a solution for this. |
I have the same problem. On our side it started to appear after adding random port to spring boot test annotation :( Not working: @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) Spring 3.2.2: |
Having the same problem, but only with some of our applications. Digging into the difference, the problem is with the The key decision point is in AbstractRequestMatcherRegistry, here. It does NOT use the MVC matcher, but picks the Delegating matcher instead. The delegating matcher does not seem to work with the MockMvc request as the servlet name is in fact null when it gets to here in DispatcherServletDelegatingRequestMatcher. AbstractRequestMatcherRegistry does say it's new since 3.2, so seems like a regression when using MockMvc in tests. |
I faced same issue in my service, the problem is when you are using:
then it delegates creating AntPathRequestMatcher to AbstractRequestMatcherRegistry here. After that the resolve method is called (this one) which checks if there is only one dispatcherServlet in context configured (here). And when you declare additional dispatcherServlet then ant and mvc mapper is wrapped within DispatcherServletDelegatingRequestMatcher which is looking for bean with the name: dispatcherServletRegistration which is initialized in LAZY MODE. In other words it is initialized after first call for different port e.g. localhost:8081/actuator/health so It can not be find in the context which has been configured during app initialization and as a result we are facing this issue. The simples fix is to just declare your own AntPathRequestMatcher and this will skip the steps I described before e.g. But still IMHO it's bug within DispatcherServletDelegatingRequestMatcher which has to be addressed and fixed |
This is not only happening in tests: I sprinkled some "AntPathRequestMatcher" calls around in my code, but imho this needs more attention as its a regression (that strangely enough not more people run into) |
So, no official response from |
As freddiN said above, this is not exclusive to tests. I used the following code as a hack for an affected application:
I can confirm that the code fix linked by @jzheaux below works like a charm. |
Because test do not always attach a fully-formulated ServletContext to the mock request, it's beneficial to consult the ServletContext presented during application startup. In case the request does have needed material, though, it maybe be helpful for Spring Security to first consult the request. Issue spring-projectsgh-14418
Thanks for the reports and for your patience as this gets addressed. A workaround for this failure is to instead use @Bean
MvcRequestMatcher.Builder mvc(HandlerMappingIntrospector introspector) {
return new MvcRequestMatcher.Builder(introspector);
}
@Bean
@Order(1)
public SecurityFilterChain dummyFilterChain(HttpSecurity http, MvcRequestMatcher.Builder mvc) throws Exception {
// ...
http.authorizeHttpRequests(config -> {
config.requestMatchers(mvc.pattern("/hello")).permitAll();
config.requestMatchers(mvc.pattern("/actuator"), mvc.pattern("/actuator/**"), mvc.pattern("/readyz"), mvc.pattern("/livez")).permitAll();
config.anyRequest().fullyAuthenticated();
});
// ...
return http.build();
} That said, I am investigating a fix to learn about its implications. I'm targeting the next milestone release to have a fix out. |
Spring Security cannot use the ServletContext attached to the ApplicationContext since there may be child ApplicationContext's with their own ServletContext. Because of that, it is necessary to always use the ServletContext attached to the request. Closes spring-projectsgh-14418
Spring Security cannot use the ServletContext attached to the ApplicationContext since there may be child ApplicationContext's with their own ServletContext. Because of that, it is necessary to always use the ServletContext attached to the request. Closes spring-projectsgh-14418
Spring Security cannot use the ServletContext attached to the ApplicationContext since there may be child ApplicationContext's with their own ServletContext. Because of that, it is necessary to always use the ServletContext attached to the request. Closes spring-projectsgh-14418
Reopening to take a look the OP's use case, which is separate from some of the non-test-related reports here. |
@atrepczik, regarding the bug you are seeing with MockMvc, the above commit will not resolve things completely. As a bit of background, when using Spring Security could possibly use the fact that it's running inside a MockMvc test, though I'm not clear of a generic way to do that. I'll reach out to the Spring Web team to see what they think. In the meantime, you can use this.mvc.perform(get("/").with((request) -> {
request.setHttpServletMapping(new MockHttpServletMapping("/", "/", "dispatcherServlet", MappingMatch.PATH));
return request;
})).andExpect(status().isOk()); Or, if you want it to be a little bit more generic, you can do: private static RequestPostProcessor mvcMapping() {
return (request) -> {
String matchValue = request.getRequestURI();
String servlet = "dispatcherServlet";
String pattern = request.getServletContext().getServletRegistration(servlet).getMappings().iterator().next();
HttpServletMapping mapping = new MockHttpServletMapping(matchValue, pattern, servlet, MappingMatch.PATH);
request.setHttpServletMapping(mapping);
return request;
};
}
// ...
this.mvc.perform(get("/").with(mvcMapping()).andExpect(status().isOk()); |
With #13849 closed, the MockMvc tests in @atrepczik's sample all pass now. Please confirm by using the latest 5.8, 6.2, 6.3, or 6.4 snapshot. |
Can confirm there's no longer an error with spring-security 6.3.1-SNAPSHOT. Thanks a lot for fixing! |
Describe the bug
In a Spring Boot application with multiple servlets registered to the context (DispatcherServlet and at least one other), an
IllegalArgumentException
with messageFailed to find servlet [] in the servlet context
is thrown when running tests withMockMvc
and@SpringBootTest(webEnvironment = RANDOM_PORT)
.The problem started occurring after an upgrade from Spring Boot 3.0.2 to Spring Boot 3.2.1 (Spring Security 6.2.1).
The exception is thrown in the DispatcherServletDelegatingRequestMatcher due to an incompatibility with the standard MockHttpServletRequest instances created with the MockMvcRequestBuilders class. These have a generic HttpRequestMapping with an empty String as the servletName, thus causing the Matcher to not find a ServletRegistration and throw the exception.
Gist with full stacktrace: Gist
To Reproduce
Run the tests in the provided sample to reproduce.
In the sample is a simple Spring Boot app with an extra servlet registered to the context and a basic security setup.
The included tests demonstrate 2 working cases and one failing case.
The test TestWithRandomPortEnvironment#expected_failure demonstrates the exception.
TestWithRandomPortEnvironment#expected_success demonstrates the fix I implemented to make this work with a RequestPostProcessor, but this solution would have to be applied everywhere I'm using MockMvc.
Expected behavior
The DispatcherServletDelegatingRequestMatcher should be compatible with the standard MockHttpServletRequest instances created with the MockMvcHttpRequestBuilders and not throw an exception.
Sample
Sample repository
The text was updated successfully, but these errors were encountered: