Skip to content

Commit c3dad06

Browse files
committed
Allow configuration of request matchers through nested builder
Issue: gh-5557
1 parent 1ad9f15 commit c3dad06

File tree

3 files changed

+252
-1
lines changed

3 files changed

+252
-1
lines changed

config/src/main/java/org/springframework/security/config/annotation/web/builders/HttpSecurity.java

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2234,6 +2234,107 @@ public RequestMatcherConfigurer requestMatchers() {
22342234
return requestMatcherConfigurer;
22352235
}
22362236

2237+
/**
2238+
* Allows specifying which {@link HttpServletRequest} instances this
2239+
* {@link HttpSecurity} will be invoked on. This method allows for easily invoking the
2240+
* {@link HttpSecurity} for multiple different {@link RequestMatcher} instances. If
2241+
* only a single {@link RequestMatcher} is necessary consider using {@link #mvcMatcher(String)},
2242+
* {@link #antMatcher(String)}, {@link #regexMatcher(String)}, or
2243+
* {@link #requestMatcher(RequestMatcher)}.
2244+
*
2245+
* <p>
2246+
* Invoking {@link #requestMatchers()} will not override previous invocations of {@link #mvcMatcher(String)}},
2247+
* {@link #requestMatchers()}, {@link #antMatcher(String)},
2248+
* {@link #regexMatcher(String)}, and {@link #requestMatcher(RequestMatcher)}.
2249+
* </p>
2250+
*
2251+
* <h3>Example Configurations</h3>
2252+
*
2253+
* The following configuration enables the {@link HttpSecurity} for URLs that begin
2254+
* with "/api/" or "/oauth/".
2255+
*
2256+
* <pre>
2257+
* &#064;Configuration
2258+
* &#064;EnableWebSecurity
2259+
* public class RequestMatchersSecurityConfig extends WebSecurityConfigurerAdapter {
2260+
*
2261+
* &#064;Override
2262+
* protected void configure(HttpSecurity http) throws Exception {
2263+
* http
2264+
* .requestMatchers(requestMatchers ->
2265+
* requestMatchers
2266+
* .antMatchers(&quot;/api/**&quot;, &quot;/oauth/**&quot;)
2267+
* )
2268+
* .authorizeRequests(authorizeRequests ->
2269+
* authorizeRequests
2270+
* .antMatchers(&quot;/**&quot;).hasRole(&quot;USER&quot;)
2271+
* )
2272+
* .httpBasic(withDefaults());
2273+
* }
2274+
* }
2275+
* </pre>
2276+
*
2277+
* The configuration below is the same as the previous configuration.
2278+
*
2279+
* <pre>
2280+
* &#064;Configuration
2281+
* &#064;EnableWebSecurity
2282+
* public class RequestMatchersSecurityConfig extends WebSecurityConfigurerAdapter {
2283+
*
2284+
* &#064;Override
2285+
* protected void configure(HttpSecurity http) throws Exception {
2286+
* http
2287+
* .requestMatchers(requestMatchers ->
2288+
* requestMatchers
2289+
* .antMatchers(&quot;/api/**&quot;)
2290+
* .antMatchers(&quot;/oauth/**&quot;)
2291+
* )
2292+
* .authorizeRequests(authorizeRequests ->
2293+
* authorizeRequests
2294+
* .antMatchers(&quot;/**&quot;).hasRole(&quot;USER&quot;)
2295+
* )
2296+
* .httpBasic(withDefaults());
2297+
* }
2298+
* }
2299+
* </pre>
2300+
*
2301+
* The configuration below is also the same as the above configuration.
2302+
*
2303+
* <pre>
2304+
* &#064;Configuration
2305+
* &#064;EnableWebSecurity
2306+
* public class RequestMatchersSecurityConfig extends WebSecurityConfigurerAdapter {
2307+
*
2308+
* &#064;Override
2309+
* protected void configure(HttpSecurity http) throws Exception {
2310+
* http
2311+
* .requestMatchers(requestMatchers ->
2312+
* requestMatchers
2313+
* .antMatchers(&quot;/api/**&quot;)
2314+
* )
2315+
* .requestMatchers(requestMatchers ->
2316+
* requestMatchers
2317+
* .antMatchers(&quot;/oauth/**&quot;)
2318+
* )
2319+
* .authorizeRequests(authorizeRequests ->
2320+
* authorizeRequests
2321+
* .antMatchers(&quot;/**&quot;).hasRole(&quot;USER&quot;)
2322+
* )
2323+
* .httpBasic(withDefaults());
2324+
* }
2325+
* }
2326+
* </pre>
2327+
*
2328+
* @param requestMatcherCustomizer the {@link Customizer} to provide more options for
2329+
* the {@link RequestMatcherConfigurer}
2330+
* @return the {@link HttpSecurity} for further customizations
2331+
* @throws Exception
2332+
*/
2333+
public HttpSecurity requestMatchers(Customizer<RequestMatcherConfigurer> requestMatcherCustomizer) throws Exception {
2334+
requestMatcherCustomizer.customize(requestMatcherConfigurer);
2335+
return HttpSecurity.this;
2336+
}
2337+
22372338
/**
22382339
* Allows configuring the {@link HttpSecurity} to only be invoked when matching the
22392340
* provided {@link RequestMatcher}. If more advanced configuration is necessary,

config/src/test/java/org/springframework/security/config/annotation/web/configurers/HttpSecurityRequestMatchersTests.java

Lines changed: 118 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2016 the original author or authors.
2+
* Copyright 2002-2019 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -38,6 +38,7 @@
3838
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
3939

4040
import static org.assertj.core.api.Assertions.assertThat;
41+
import static org.springframework.security.config.Customizer.withDefaults;
4142

4243
/**
4344
* @author Rob Winch
@@ -195,6 +196,62 @@ public String path() {
195196
}
196197
}
197198

199+
@Test
200+
public void requestMatchersWhenMvcMatcherInLambdaThenPathIsSecured() throws Exception {
201+
loadConfig(RequestMatchersMvcMatcherInLambdaConfig.class);
202+
203+
this.request.setServletPath("/path");
204+
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
205+
206+
assertThat(this.response.getStatus())
207+
.isEqualTo(HttpServletResponse.SC_UNAUTHORIZED);
208+
209+
setup();
210+
211+
this.request.setServletPath("/path.html");
212+
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
213+
214+
assertThat(this.response.getStatus())
215+
.isEqualTo(HttpServletResponse.SC_UNAUTHORIZED);
216+
217+
setup();
218+
219+
this.request.setServletPath("/path/");
220+
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
221+
222+
assertThat(this.response.getStatus())
223+
.isEqualTo(HttpServletResponse.SC_UNAUTHORIZED);
224+
}
225+
226+
@EnableWebSecurity
227+
@Configuration
228+
@EnableWebMvc
229+
static class RequestMatchersMvcMatcherInLambdaConfig extends WebSecurityConfigurerAdapter {
230+
@Override
231+
protected void configure(HttpSecurity http) throws Exception {
232+
// @formatter:off
233+
http
234+
.requestMatchers(requestMatchers ->
235+
requestMatchers
236+
.mvcMatchers("/path")
237+
)
238+
.httpBasic(withDefaults())
239+
.authorizeRequests(authorizeRequests ->
240+
authorizeRequests
241+
.anyRequest().denyAll()
242+
);
243+
// @formatter:on
244+
}
245+
246+
@RestController
247+
static class PathController {
248+
@RequestMapping("/path")
249+
public String path() {
250+
return "path";
251+
}
252+
}
253+
}
254+
198255
@Test
199256
public void requestMatchersMvcMatcherServletPath() throws Exception {
200257
loadConfig(RequestMatchersMvcMatcherServeltPathConfig.class);
@@ -260,6 +317,66 @@ public String path() {
260317
}
261318
}
262319

320+
@Test
321+
public void requestMatcherWhensMvcMatcherServletPathInLambdaThenPathIsSecured() throws Exception {
322+
loadConfig(RequestMatchersMvcMatcherServletPathInLambdaConfig.class);
323+
324+
this.request.setServletPath("/spring");
325+
this.request.setRequestURI("/spring/path");
326+
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
327+
328+
assertThat(this.response.getStatus())
329+
.isEqualTo(HttpServletResponse.SC_UNAUTHORIZED);
330+
331+
setup();
332+
333+
this.request.setServletPath("");
334+
this.request.setRequestURI("/path");
335+
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
336+
337+
assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_OK);
338+
339+
setup();
340+
341+
this.request.setServletPath("/other");
342+
this.request.setRequestURI("/other/path");
343+
344+
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
345+
346+
assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_OK);
347+
}
348+
349+
@EnableWebSecurity
350+
@Configuration
351+
@EnableWebMvc
352+
static class RequestMatchersMvcMatcherServletPathInLambdaConfig
353+
extends WebSecurityConfigurerAdapter {
354+
@Override
355+
protected void configure(HttpSecurity http) throws Exception {
356+
// @formatter:off
357+
http
358+
.requestMatchers(requestMatchers ->
359+
requestMatchers
360+
.mvcMatchers("/path").servletPath("/spring")
361+
.mvcMatchers("/never-match")
362+
)
363+
.httpBasic(withDefaults())
364+
.authorizeRequests(authorizeRequests ->
365+
authorizeRequests
366+
.anyRequest().denyAll()
367+
);
368+
// @formatter:on
369+
}
370+
371+
@RestController
372+
static class PathController {
373+
@RequestMapping("/path")
374+
public String path() {
375+
return "path";
376+
}
377+
}
378+
}
379+
263380
public void loadConfig(Class<?>... configs) {
264381
this.context = new AnnotationConfigWebApplicationContext();
265382
this.context.register(configs);

config/src/test/java/org/springframework/security/config/annotation/web/configurers/RequestMatcherConfigurerTests.java

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,4 +71,37 @@ protected void configure(HttpSecurity http) throws Exception {
7171
// @formatter:on
7272
}
7373
}
74+
75+
@Test
76+
public void authorizeRequestsWhenInvokedMultipleTimesInLambdaThenChainsPaths() throws Exception {
77+
this.spring.register(AuthorizeRequestInLambdaConfig.class).autowire();
78+
79+
this.mvc.perform(get("/oauth/abc"))
80+
.andExpect(status().isForbidden());
81+
this.mvc.perform(get("/api/abc"))
82+
.andExpect(status().isForbidden());
83+
}
84+
85+
@EnableWebSecurity
86+
static class AuthorizeRequestInLambdaConfig extends WebSecurityConfigurerAdapter {
87+
88+
@Override
89+
protected void configure(HttpSecurity http) throws Exception {
90+
// @formatter:off
91+
http
92+
.requestMatchers(requestMatchers ->
93+
requestMatchers
94+
.antMatchers("/api/**")
95+
)
96+
.requestMatchers(requestMatchers ->
97+
requestMatchers
98+
.antMatchers("/oauth/**")
99+
)
100+
.authorizeRequests(authorizeRequests ->
101+
authorizeRequests
102+
.anyRequest().denyAll()
103+
);
104+
// @formatter:on
105+
}
106+
}
74107
}

0 commit comments

Comments
 (0)