Skip to content

Commit 6aabd76

Browse files
committed
Pick MvcRequestMatcher for MockMvc requests
Closes gh-13849
1 parent cdd6266 commit 6aabd76

File tree

3 files changed

+120
-27
lines changed

3 files changed

+120
-27
lines changed

config/src/main/java/org/springframework/security/config/annotation/web/AbstractRequestMatcherRegistry.java

Lines changed: 54 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -44,11 +44,13 @@
4444
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
4545
import org.springframework.security.web.util.matcher.AnyRequestMatcher;
4646
import org.springframework.security.web.util.matcher.DispatcherTypeRequestMatcher;
47+
import org.springframework.security.web.util.matcher.OrRequestMatcher;
4748
import org.springframework.security.web.util.matcher.RegexRequestMatcher;
4849
import org.springframework.security.web.util.matcher.RequestMatcher;
4950
import org.springframework.util.Assert;
5051
import org.springframework.util.ClassUtils;
5152
import org.springframework.web.context.WebApplicationContext;
53+
import org.springframework.web.servlet.DispatcherServlet;
5254
import org.springframework.web.servlet.handler.HandlerMappingIntrospector;
5355

5456
/**
@@ -335,10 +337,10 @@ public C requestMatchers(HttpMethod method, String... patterns) {
335337
private RequestMatcher resolve(AntPathRequestMatcher ant, MvcRequestMatcher mvc, ServletContext servletContext) {
336338
Map<String, ? extends ServletRegistration> registrations = mappableServletRegistrations(servletContext);
337339
if (registrations.isEmpty()) {
338-
return ant;
340+
return new DispatcherServletDelegatingRequestMatcher(ant, mvc, new MockMvcRequestMatcher());
339341
}
340342
if (!hasDispatcherServlet(registrations)) {
341-
return ant;
343+
return new DispatcherServletDelegatingRequestMatcher(ant, mvc, new MockMvcRequestMatcher());
342344
}
343345
ServletRegistration dispatcherServlet = requireOneRootDispatcherServlet(registrations);
344346
if (dispatcherServlet != null) {
@@ -605,55 +607,83 @@ public String toString() {
605607

606608
}
607609

610+
static class MockMvcRequestMatcher implements RequestMatcher {
611+
612+
@Override
613+
public boolean matches(HttpServletRequest request) {
614+
return request.getAttribute("org.springframework.test.web.servlet.MockMvc.MVC_RESULT_ATTRIBUTE") != null;
615+
}
616+
617+
}
618+
619+
static class DispatcherServletRequestMatcher implements RequestMatcher {
620+
621+
private final ServletContext servletContext;
622+
623+
DispatcherServletRequestMatcher(ServletContext servletContext) {
624+
this.servletContext = servletContext;
625+
}
626+
627+
@Override
628+
public boolean matches(HttpServletRequest request) {
629+
String name = request.getHttpServletMapping().getServletName();
630+
ServletRegistration registration = this.servletContext.getServletRegistration(name);
631+
Assert.notNull(name, "Failed to find servlet [" + name + "] in the servlet context");
632+
try {
633+
Class<?> clazz = Class.forName(registration.getClassName());
634+
return DispatcherServlet.class.isAssignableFrom(clazz);
635+
}
636+
catch (ClassNotFoundException ex) {
637+
return false;
638+
}
639+
}
640+
641+
}
642+
608643
static class DispatcherServletDelegatingRequestMatcher implements RequestMatcher {
609644

610645
private final AntPathRequestMatcher ant;
611646

612647
private final MvcRequestMatcher mvc;
613648

614-
private final ServletContext servletContext;
649+
private final RequestMatcher dispatcherServlet;
615650

616651
DispatcherServletDelegatingRequestMatcher(AntPathRequestMatcher ant, MvcRequestMatcher mvc,
617652
ServletContext servletContext) {
653+
this(ant, mvc, new OrRequestMatcher(new MockMvcRequestMatcher(),
654+
new DispatcherServletRequestMatcher(servletContext)));
655+
}
656+
657+
DispatcherServletDelegatingRequestMatcher(AntPathRequestMatcher ant, MvcRequestMatcher mvc,
658+
RequestMatcher dispatcherServlet) {
618659
this.ant = ant;
619660
this.mvc = mvc;
620-
this.servletContext = servletContext;
661+
this.dispatcherServlet = dispatcherServlet;
662+
}
663+
664+
RequestMatcher requestMatcher(HttpServletRequest request) {
665+
if (this.dispatcherServlet.matches(request)) {
666+
return this.mvc;
667+
}
668+
return this.ant;
621669
}
622670

623671
@Override
624672
public boolean matches(HttpServletRequest request) {
625-
String name = request.getHttpServletMapping().getServletName();
626-
ServletRegistration registration = this.servletContext.getServletRegistration(name);
627-
Assert.notNull(registration, "Failed to find servlet [" + name + "] in the servlet context");
628-
if (isDispatcherServlet(registration)) {
673+
if (this.dispatcherServlet.matches(request)) {
629674
return this.mvc.matches(request);
630675
}
631676
return this.ant.matches(request);
632677
}
633678

634679
@Override
635680
public MatchResult matcher(HttpServletRequest request) {
636-
String name = request.getHttpServletMapping().getServletName();
637-
ServletRegistration registration = this.servletContext.getServletRegistration(name);
638-
Assert.notNull(registration, "Failed to find servlet [" + name + "] in the servlet context");
639-
if (isDispatcherServlet(registration)) {
681+
if (this.dispatcherServlet.matches(request)) {
640682
return this.mvc.matcher(request);
641683
}
642684
return this.ant.matcher(request);
643685
}
644686

645-
private boolean isDispatcherServlet(ServletRegistration registration) {
646-
Class<?> dispatcherServlet = ClassUtils
647-
.resolveClassName("org.springframework.web.servlet.DispatcherServlet", null);
648-
try {
649-
Class<?> clazz = Class.forName(registration.getClassName());
650-
return dispatcherServlet.isAssignableFrom(clazz);
651-
}
652-
catch (ClassNotFoundException ex) {
653-
return false;
654-
}
655-
}
656-
657687
@Override
658688
public String toString() {
659689
return "DispatcherServletDelegating [" + "ant = " + this.ant + ", mvc = " + this.mvc + "]";

config/src/test/java/org/springframework/security/config/annotation/web/AbstractRequestMatcherRegistryTests.java

Lines changed: 65 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,27 +30,35 @@
3030

3131
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
3232
import org.springframework.context.ApplicationContext;
33+
import org.springframework.context.annotation.Configuration;
3334
import org.springframework.http.HttpMethod;
3435
import org.springframework.mock.web.MockHttpServletRequest;
3536
import org.springframework.security.config.MockServletContext;
3637
import org.springframework.security.config.TestMockHttpServletMappings;
3738
import org.springframework.security.config.annotation.ObjectPostProcessor;
3839
import org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry.DispatcherServletDelegatingRequestMatcher;
40+
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
41+
import org.springframework.security.config.test.SpringTestContext;
3942
import org.springframework.security.web.servlet.util.matcher.MvcRequestMatcher;
4043
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
4144
import org.springframework.security.web.util.matcher.DispatcherTypeRequestMatcher;
4245
import org.springframework.security.web.util.matcher.RegexRequestMatcher;
4346
import org.springframework.security.web.util.matcher.RequestMatcher;
47+
import org.springframework.test.web.servlet.MockMvc;
48+
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
4449
import org.springframework.web.context.WebApplicationContext;
4550
import org.springframework.web.servlet.DispatcherServlet;
51+
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
4652

4753
import static org.assertj.core.api.Assertions.assertThat;
4854
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
55+
import static org.assertj.core.api.InstanceOfAssertFactories.type;
4956
import static org.mockito.BDDMockito.given;
5057
import static org.mockito.Mockito.mock;
5158
import static org.mockito.Mockito.verify;
5259
import static org.mockito.Mockito.verifyNoInteractions;
5360
import static org.mockito.Mockito.verifyNoMoreInteractions;
61+
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
5462

5563
/**
5664
* Tests for {@link AbstractRequestMatcherRegistry}.
@@ -206,18 +214,65 @@ public void requestMatchersWhenNoDispatcherServletThenAntPathRequestMatcherType(
206214
mockMvcIntrospector(true);
207215
MockServletContext servletContext = new MockServletContext();
208216
given(this.context.getServletContext()).willReturn(servletContext);
217+
MockHttpServletRequest request = new MockHttpServletRequest();
218+
List<RequestMatcher> requestMatchers = this.matcherRegistry.requestMatchers("/**");
219+
assertThat(requestMatchers).isNotEmpty();
220+
assertThat(requestMatchers).hasSize(1);
221+
assertThat(requestMatchers.get(0)).asInstanceOf(type(DispatcherServletDelegatingRequestMatcher.class))
222+
.extracting((matcher) -> matcher.requestMatcher(request))
223+
.isInstanceOf(AntPathRequestMatcher.class);
209224
servletContext.addServlet("servletOne", Servlet.class).addMapping("/one");
210225
servletContext.addServlet("servletTwo", Servlet.class).addMapping("/two");
211-
List<RequestMatcher> requestMatchers = this.matcherRegistry.requestMatchers("/**");
226+
requestMatchers = this.matcherRegistry.requestMatchers("/**");
212227
assertThat(requestMatchers).isNotEmpty();
213228
assertThat(requestMatchers).hasSize(1);
214-
assertThat(requestMatchers.get(0)).isExactlyInstanceOf(AntPathRequestMatcher.class);
229+
assertThat(requestMatchers.get(0)).asInstanceOf(type(DispatcherServletDelegatingRequestMatcher.class))
230+
.extracting((matcher) -> matcher.requestMatcher(request))
231+
.isInstanceOf(AntPathRequestMatcher.class);
215232
servletContext.addServlet("servletOne", Servlet.class);
216233
servletContext.addServlet("servletTwo", Servlet.class);
217234
requestMatchers = this.matcherRegistry.requestMatchers("/**");
218235
assertThat(requestMatchers).isNotEmpty();
219236
assertThat(requestMatchers).hasSize(1);
220-
assertThat(requestMatchers.get(0)).isExactlyInstanceOf(AntPathRequestMatcher.class);
237+
assertThat(requestMatchers.get(0)).asInstanceOf(type(DispatcherServletDelegatingRequestMatcher.class))
238+
.extracting((matcher) -> matcher.requestMatcher(request))
239+
.isInstanceOf(AntPathRequestMatcher.class);
240+
}
241+
242+
// gh-14418
243+
@Test
244+
public void requestMatchersWhenNoDispatcherServletMockMvcThenMvcRequestMatcherType() throws Exception {
245+
MockServletContext servletContext = new MockServletContext();
246+
try (SpringTestContext spring = new SpringTestContext(this)) {
247+
spring.register(MockMvcConfiguration.class)
248+
.postProcessor((context) -> context.setServletContext(servletContext))
249+
.autowire();
250+
this.matcherRegistry.setApplicationContext(spring.getContext());
251+
MockMvc mvc = MockMvcBuilders.webAppContextSetup(spring.getContext()).build();
252+
MockHttpServletRequest request = mvc.perform(get("/")).andReturn().getRequest();
253+
List<RequestMatcher> requestMatchers = this.matcherRegistry.requestMatchers("/**");
254+
assertThat(requestMatchers).isNotEmpty();
255+
assertThat(requestMatchers).hasSize(1);
256+
assertThat(requestMatchers.get(0)).asInstanceOf(type(DispatcherServletDelegatingRequestMatcher.class))
257+
.extracting((matcher) -> matcher.requestMatcher(request))
258+
.isInstanceOf(MvcRequestMatcher.class);
259+
servletContext.addServlet("servletOne", Servlet.class).addMapping("/one");
260+
servletContext.addServlet("servletTwo", Servlet.class).addMapping("/two");
261+
requestMatchers = this.matcherRegistry.requestMatchers("/**");
262+
assertThat(requestMatchers).isNotEmpty();
263+
assertThat(requestMatchers).hasSize(1);
264+
assertThat(requestMatchers.get(0)).asInstanceOf(type(DispatcherServletDelegatingRequestMatcher.class))
265+
.extracting((matcher) -> matcher.requestMatcher(request))
266+
.isInstanceOf(MvcRequestMatcher.class);
267+
servletContext.addServlet("servletOne", Servlet.class);
268+
servletContext.addServlet("servletTwo", Servlet.class);
269+
requestMatchers = this.matcherRegistry.requestMatchers("/**");
270+
assertThat(requestMatchers).isNotEmpty();
271+
assertThat(requestMatchers).hasSize(1);
272+
assertThat(requestMatchers.get(0)).asInstanceOf(type(DispatcherServletDelegatingRequestMatcher.class))
273+
.extracting((matcher) -> matcher.requestMatcher(request))
274+
.isInstanceOf(MvcRequestMatcher.class);
275+
}
221276
}
222277

223278
@Test
@@ -398,4 +453,11 @@ private List<RequestMatcher> unwrap(List<RequestMatcher> wrappedMatchers) {
398453

399454
}
400455

456+
@Configuration
457+
@EnableWebSecurity
458+
@EnableWebMvc
459+
static class MockMvcConfiguration {
460+
461+
}
462+
401463
}

etc/checkstyle/checkstyle.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
<property name="avoidStaticImportExcludes" value="org.springframework.security.web.csrf.CsrfTokenAssert.*" />
2121
<property name="avoidStaticImportExcludes" value="org.springframework.security.web.util.matcher.AntPathRequestMatcher.*" />
2222
<property name="avoidStaticImportExcludes" value="org.springframework.security.web.util.matcher.RegexRequestMatcher.*" />
23+
<property name="avoidStaticImportExcludes" value="org.assertj.core.api.InstanceOfAssertFactories.*"/>
2324
</module>
2425
<module name="com.puppycrawl.tools.checkstyle.TreeWalker">
2526
<module name="com.puppycrawl.tools.checkstyle.checks.regexp.RegexpSinglelineJavaCheck">

0 commit comments

Comments
 (0)