Allow configuration of request matchers through nested builder
Issue: gh-5557
This commit is contained in:
parent
1ad9f15e19
commit
c3dad06ea6
|
@ -2234,6 +2234,107 @@ public final class HttpSecurity extends
|
||||||
return requestMatcherConfigurer;
|
return requestMatcherConfigurer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allows specifying which {@link HttpServletRequest} instances this
|
||||||
|
* {@link HttpSecurity} will be invoked on. This method allows for easily invoking the
|
||||||
|
* {@link HttpSecurity} for multiple different {@link RequestMatcher} instances. If
|
||||||
|
* only a single {@link RequestMatcher} is necessary consider using {@link #mvcMatcher(String)},
|
||||||
|
* {@link #antMatcher(String)}, {@link #regexMatcher(String)}, or
|
||||||
|
* {@link #requestMatcher(RequestMatcher)}.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Invoking {@link #requestMatchers()} will not override previous invocations of {@link #mvcMatcher(String)}},
|
||||||
|
* {@link #requestMatchers()}, {@link #antMatcher(String)},
|
||||||
|
* {@link #regexMatcher(String)}, and {@link #requestMatcher(RequestMatcher)}.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <h3>Example Configurations</h3>
|
||||||
|
*
|
||||||
|
* The following configuration enables the {@link HttpSecurity} for URLs that begin
|
||||||
|
* with "/api/" or "/oauth/".
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* @Configuration
|
||||||
|
* @EnableWebSecurity
|
||||||
|
* public class RequestMatchersSecurityConfig extends WebSecurityConfigurerAdapter {
|
||||||
|
*
|
||||||
|
* @Override
|
||||||
|
* protected void configure(HttpSecurity http) throws Exception {
|
||||||
|
* http
|
||||||
|
* .requestMatchers(requestMatchers ->
|
||||||
|
* requestMatchers
|
||||||
|
* .antMatchers("/api/**", "/oauth/**")
|
||||||
|
* )
|
||||||
|
* .authorizeRequests(authorizeRequests ->
|
||||||
|
* authorizeRequests
|
||||||
|
* .antMatchers("/**").hasRole("USER")
|
||||||
|
* )
|
||||||
|
* .httpBasic(withDefaults());
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* The configuration below is the same as the previous configuration.
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* @Configuration
|
||||||
|
* @EnableWebSecurity
|
||||||
|
* public class RequestMatchersSecurityConfig extends WebSecurityConfigurerAdapter {
|
||||||
|
*
|
||||||
|
* @Override
|
||||||
|
* protected void configure(HttpSecurity http) throws Exception {
|
||||||
|
* http
|
||||||
|
* .requestMatchers(requestMatchers ->
|
||||||
|
* requestMatchers
|
||||||
|
* .antMatchers("/api/**")
|
||||||
|
* .antMatchers("/oauth/**")
|
||||||
|
* )
|
||||||
|
* .authorizeRequests(authorizeRequests ->
|
||||||
|
* authorizeRequests
|
||||||
|
* .antMatchers("/**").hasRole("USER")
|
||||||
|
* )
|
||||||
|
* .httpBasic(withDefaults());
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* The configuration below is also the same as the above configuration.
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* @Configuration
|
||||||
|
* @EnableWebSecurity
|
||||||
|
* public class RequestMatchersSecurityConfig extends WebSecurityConfigurerAdapter {
|
||||||
|
*
|
||||||
|
* @Override
|
||||||
|
* protected void configure(HttpSecurity http) throws Exception {
|
||||||
|
* http
|
||||||
|
* .requestMatchers(requestMatchers ->
|
||||||
|
* requestMatchers
|
||||||
|
* .antMatchers("/api/**")
|
||||||
|
* )
|
||||||
|
* .requestMatchers(requestMatchers ->
|
||||||
|
* requestMatchers
|
||||||
|
* .antMatchers("/oauth/**")
|
||||||
|
* )
|
||||||
|
* .authorizeRequests(authorizeRequests ->
|
||||||
|
* authorizeRequests
|
||||||
|
* .antMatchers("/**").hasRole("USER")
|
||||||
|
* )
|
||||||
|
* .httpBasic(withDefaults());
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @param requestMatcherCustomizer the {@link Customizer} to provide more options for
|
||||||
|
* the {@link RequestMatcherConfigurer}
|
||||||
|
* @return the {@link HttpSecurity} for further customizations
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
public HttpSecurity requestMatchers(Customizer<RequestMatcherConfigurer> requestMatcherCustomizer) throws Exception {
|
||||||
|
requestMatcherCustomizer.customize(requestMatcherConfigurer);
|
||||||
|
return HttpSecurity.this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Allows configuring the {@link HttpSecurity} to only be invoked when matching the
|
* Allows configuring the {@link HttpSecurity} to only be invoked when matching the
|
||||||
* provided {@link RequestMatcher}. If more advanced configuration is necessary,
|
* provided {@link RequestMatcher}. If more advanced configuration is necessary,
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2016 the original author or authors.
|
* Copyright 2002-2019 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -38,6 +38,7 @@ import org.springframework.web.context.support.AnnotationConfigWebApplicationCon
|
||||||
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
|
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.springframework.security.config.Customizer.withDefaults;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Rob Winch
|
* @author Rob Winch
|
||||||
|
@ -195,6 +196,62 @@ public class HttpSecurityRequestMatchersTests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void requestMatchersWhenMvcMatcherInLambdaThenPathIsSecured() throws Exception {
|
||||||
|
loadConfig(RequestMatchersMvcMatcherInLambdaConfig.class);
|
||||||
|
|
||||||
|
this.request.setServletPath("/path");
|
||||||
|
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
|
||||||
|
|
||||||
|
assertThat(this.response.getStatus())
|
||||||
|
.isEqualTo(HttpServletResponse.SC_UNAUTHORIZED);
|
||||||
|
|
||||||
|
setup();
|
||||||
|
|
||||||
|
this.request.setServletPath("/path.html");
|
||||||
|
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
|
||||||
|
|
||||||
|
assertThat(this.response.getStatus())
|
||||||
|
.isEqualTo(HttpServletResponse.SC_UNAUTHORIZED);
|
||||||
|
|
||||||
|
setup();
|
||||||
|
|
||||||
|
this.request.setServletPath("/path/");
|
||||||
|
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
|
||||||
|
|
||||||
|
assertThat(this.response.getStatus())
|
||||||
|
.isEqualTo(HttpServletResponse.SC_UNAUTHORIZED);
|
||||||
|
}
|
||||||
|
|
||||||
|
@EnableWebSecurity
|
||||||
|
@Configuration
|
||||||
|
@EnableWebMvc
|
||||||
|
static class RequestMatchersMvcMatcherInLambdaConfig extends WebSecurityConfigurerAdapter {
|
||||||
|
@Override
|
||||||
|
protected void configure(HttpSecurity http) throws Exception {
|
||||||
|
// @formatter:off
|
||||||
|
http
|
||||||
|
.requestMatchers(requestMatchers ->
|
||||||
|
requestMatchers
|
||||||
|
.mvcMatchers("/path")
|
||||||
|
)
|
||||||
|
.httpBasic(withDefaults())
|
||||||
|
.authorizeRequests(authorizeRequests ->
|
||||||
|
authorizeRequests
|
||||||
|
.anyRequest().denyAll()
|
||||||
|
);
|
||||||
|
// @formatter:on
|
||||||
|
}
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
static class PathController {
|
||||||
|
@RequestMapping("/path")
|
||||||
|
public String path() {
|
||||||
|
return "path";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void requestMatchersMvcMatcherServletPath() throws Exception {
|
public void requestMatchersMvcMatcherServletPath() throws Exception {
|
||||||
loadConfig(RequestMatchersMvcMatcherServeltPathConfig.class);
|
loadConfig(RequestMatchersMvcMatcherServeltPathConfig.class);
|
||||||
|
@ -260,6 +317,66 @@ public class HttpSecurityRequestMatchersTests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void requestMatcherWhensMvcMatcherServletPathInLambdaThenPathIsSecured() throws Exception {
|
||||||
|
loadConfig(RequestMatchersMvcMatcherServletPathInLambdaConfig.class);
|
||||||
|
|
||||||
|
this.request.setServletPath("/spring");
|
||||||
|
this.request.setRequestURI("/spring/path");
|
||||||
|
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
|
||||||
|
|
||||||
|
assertThat(this.response.getStatus())
|
||||||
|
.isEqualTo(HttpServletResponse.SC_UNAUTHORIZED);
|
||||||
|
|
||||||
|
setup();
|
||||||
|
|
||||||
|
this.request.setServletPath("");
|
||||||
|
this.request.setRequestURI("/path");
|
||||||
|
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
|
||||||
|
|
||||||
|
assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_OK);
|
||||||
|
|
||||||
|
setup();
|
||||||
|
|
||||||
|
this.request.setServletPath("/other");
|
||||||
|
this.request.setRequestURI("/other/path");
|
||||||
|
|
||||||
|
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
|
||||||
|
|
||||||
|
assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_OK);
|
||||||
|
}
|
||||||
|
|
||||||
|
@EnableWebSecurity
|
||||||
|
@Configuration
|
||||||
|
@EnableWebMvc
|
||||||
|
static class RequestMatchersMvcMatcherServletPathInLambdaConfig
|
||||||
|
extends WebSecurityConfigurerAdapter {
|
||||||
|
@Override
|
||||||
|
protected void configure(HttpSecurity http) throws Exception {
|
||||||
|
// @formatter:off
|
||||||
|
http
|
||||||
|
.requestMatchers(requestMatchers ->
|
||||||
|
requestMatchers
|
||||||
|
.mvcMatchers("/path").servletPath("/spring")
|
||||||
|
.mvcMatchers("/never-match")
|
||||||
|
)
|
||||||
|
.httpBasic(withDefaults())
|
||||||
|
.authorizeRequests(authorizeRequests ->
|
||||||
|
authorizeRequests
|
||||||
|
.anyRequest().denyAll()
|
||||||
|
);
|
||||||
|
// @formatter:on
|
||||||
|
}
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
static class PathController {
|
||||||
|
@RequestMapping("/path")
|
||||||
|
public String path() {
|
||||||
|
return "path";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void loadConfig(Class<?>... configs) {
|
public void loadConfig(Class<?>... configs) {
|
||||||
this.context = new AnnotationConfigWebApplicationContext();
|
this.context = new AnnotationConfigWebApplicationContext();
|
||||||
this.context.register(configs);
|
this.context.register(configs);
|
||||||
|
|
|
@ -71,4 +71,37 @@ public class RequestMatcherConfigurerTests {
|
||||||
// @formatter:on
|
// @formatter:on
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void authorizeRequestsWhenInvokedMultipleTimesInLambdaThenChainsPaths() throws Exception {
|
||||||
|
this.spring.register(AuthorizeRequestInLambdaConfig.class).autowire();
|
||||||
|
|
||||||
|
this.mvc.perform(get("/oauth/abc"))
|
||||||
|
.andExpect(status().isForbidden());
|
||||||
|
this.mvc.perform(get("/api/abc"))
|
||||||
|
.andExpect(status().isForbidden());
|
||||||
|
}
|
||||||
|
|
||||||
|
@EnableWebSecurity
|
||||||
|
static class AuthorizeRequestInLambdaConfig extends WebSecurityConfigurerAdapter {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void configure(HttpSecurity http) throws Exception {
|
||||||
|
// @formatter:off
|
||||||
|
http
|
||||||
|
.requestMatchers(requestMatchers ->
|
||||||
|
requestMatchers
|
||||||
|
.antMatchers("/api/**")
|
||||||
|
)
|
||||||
|
.requestMatchers(requestMatchers ->
|
||||||
|
requestMatchers
|
||||||
|
.antMatchers("/oauth/**")
|
||||||
|
)
|
||||||
|
.authorizeRequests(authorizeRequests ->
|
||||||
|
authorizeRequests
|
||||||
|
.anyRequest().denyAll()
|
||||||
|
);
|
||||||
|
// @formatter:on
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue