Merge branch '5.8.x'
Closes gh-11347 in 6.0.x Closes gh-11945
This commit is contained in:
commit
ad2abd39dc
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2019 the original author or authors.
|
* Copyright 2002-2022 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.
|
||||||
|
@ -35,6 +35,7 @@ import org.springframework.security.web.util.matcher.DispatcherTypeRequestMatche
|
||||||
import org.springframework.security.web.util.matcher.RegexRequestMatcher;
|
import org.springframework.security.web.util.matcher.RegexRequestMatcher;
|
||||||
import org.springframework.security.web.util.matcher.RequestMatcher;
|
import org.springframework.security.web.util.matcher.RequestMatcher;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
|
import org.springframework.util.ClassUtils;
|
||||||
import org.springframework.web.servlet.handler.HandlerMappingIntrospector;
|
import org.springframework.web.servlet.handler.HandlerMappingIntrospector;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -50,12 +51,21 @@ public abstract class AbstractRequestMatcherRegistry<C> {
|
||||||
|
|
||||||
private static final String HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME = "mvcHandlerMappingIntrospector";
|
private static final String HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME = "mvcHandlerMappingIntrospector";
|
||||||
|
|
||||||
|
private static final String HANDLER_MAPPING_INTROSPECTOR = "org.springframework.web.servlet.handler.HandlerMappingIntrospector";
|
||||||
|
|
||||||
|
private static final boolean mvcPresent;
|
||||||
|
|
||||||
private static final RequestMatcher ANY_REQUEST = AnyRequestMatcher.INSTANCE;
|
private static final RequestMatcher ANY_REQUEST = AnyRequestMatcher.INSTANCE;
|
||||||
|
|
||||||
private ApplicationContext context;
|
private ApplicationContext context;
|
||||||
|
|
||||||
private boolean anyRequestConfigured = false;
|
private boolean anyRequestConfigured = false;
|
||||||
|
|
||||||
|
static {
|
||||||
|
mvcPresent = ClassUtils.isPresent(HANDLER_MAPPING_INTROSPECTOR,
|
||||||
|
AbstractRequestMatcherRegistry.class.getClassLoader());
|
||||||
|
}
|
||||||
|
|
||||||
protected final void setApplicationContext(ApplicationContext context) {
|
protected final void setApplicationContext(ApplicationContext context) {
|
||||||
this.context = context;
|
this.context = context;
|
||||||
}
|
}
|
||||||
|
@ -85,7 +95,9 @@ public abstract class AbstractRequestMatcherRegistry<C> {
|
||||||
* instances.
|
* instances.
|
||||||
* @param method the {@link HttpMethod} to use for any {@link HttpMethod}.
|
* @param method the {@link HttpMethod} to use for any {@link HttpMethod}.
|
||||||
* @return the object that is chained after creating the {@link RequestMatcher}
|
* @return the object that is chained after creating the {@link RequestMatcher}
|
||||||
|
* @deprecated use {@link #requestMatchers(HttpMethod)} instead
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public C antMatchers(HttpMethod method) {
|
public C antMatchers(HttpMethod method) {
|
||||||
return antMatchers(method, "/**");
|
return antMatchers(method, "/**");
|
||||||
}
|
}
|
||||||
|
@ -99,7 +111,9 @@ public abstract class AbstractRequestMatcherRegistry<C> {
|
||||||
* @param antPatterns the ant patterns to create. If {@code null} or empty, then
|
* @param antPatterns the ant patterns to create. If {@code null} or empty, then
|
||||||
* matches on nothing.
|
* matches on nothing.
|
||||||
* @return the object that is chained after creating the {@link RequestMatcher}
|
* @return the object that is chained after creating the {@link RequestMatcher}
|
||||||
|
* @deprecated use {@link #requestMatchers(HttpMethod, String...)} instead
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public C antMatchers(HttpMethod method, String... antPatterns) {
|
public C antMatchers(HttpMethod method, String... antPatterns) {
|
||||||
Assert.state(!this.anyRequestConfigured, "Can't configure antMatchers after anyRequest");
|
Assert.state(!this.anyRequestConfigured, "Can't configure antMatchers after anyRequest");
|
||||||
return chainRequestMatchers(RequestMatchers.antMatchers(method, antPatterns));
|
return chainRequestMatchers(RequestMatchers.antMatchers(method, antPatterns));
|
||||||
|
@ -112,7 +126,9 @@ public abstract class AbstractRequestMatcherRegistry<C> {
|
||||||
* @param antPatterns the ant patterns to create
|
* @param antPatterns the ant patterns to create
|
||||||
* {@link org.springframework.security.web.util.matcher.AntPathRequestMatcher} from
|
* {@link org.springframework.security.web.util.matcher.AntPathRequestMatcher} from
|
||||||
* @return the object that is chained after creating the {@link RequestMatcher}
|
* @return the object that is chained after creating the {@link RequestMatcher}
|
||||||
|
* @deprecated use {@link #requestMatchers(String...)} instead
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public C antMatchers(String... antPatterns) {
|
public C antMatchers(String... antPatterns) {
|
||||||
Assert.state(!this.anyRequestConfigured, "Can't configure antMatchers after anyRequest");
|
Assert.state(!this.anyRequestConfigured, "Can't configure antMatchers after anyRequest");
|
||||||
return chainRequestMatchers(RequestMatchers.antMatchers(antPatterns));
|
return chainRequestMatchers(RequestMatchers.antMatchers(antPatterns));
|
||||||
|
@ -132,7 +148,9 @@ public abstract class AbstractRequestMatcherRegistry<C> {
|
||||||
* @param mvcPatterns the patterns to match on. The rules for matching are defined by
|
* @param mvcPatterns the patterns to match on. The rules for matching are defined by
|
||||||
* Spring MVC
|
* Spring MVC
|
||||||
* @return the object that is chained after creating the {@link RequestMatcher}.
|
* @return the object that is chained after creating the {@link RequestMatcher}.
|
||||||
|
* @deprecated use {@link #requestMatchers(String...)} instead
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public abstract C mvcMatchers(String... mvcPatterns);
|
public abstract C mvcMatchers(String... mvcPatterns);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -150,7 +168,9 @@ public abstract class AbstractRequestMatcherRegistry<C> {
|
||||||
* @param mvcPatterns the patterns to match on. The rules for matching are defined by
|
* @param mvcPatterns the patterns to match on. The rules for matching are defined by
|
||||||
* Spring MVC
|
* Spring MVC
|
||||||
* @return the object that is chained after creating the {@link RequestMatcher}.
|
* @return the object that is chained after creating the {@link RequestMatcher}.
|
||||||
|
* @deprecated use {@link #requestMatchers(HttpMethod, String...)} instead
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public abstract C mvcMatchers(HttpMethod method, String... mvcPatterns);
|
public abstract C mvcMatchers(HttpMethod method, String... mvcPatterns);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -190,7 +210,10 @@ public abstract class AbstractRequestMatcherRegistry<C> {
|
||||||
* @param regexPatterns the regular expressions to create
|
* @param regexPatterns the regular expressions to create
|
||||||
* {@link org.springframework.security.web.util.matcher.RegexRequestMatcher} from
|
* {@link org.springframework.security.web.util.matcher.RegexRequestMatcher} from
|
||||||
* @return the object that is chained after creating the {@link RequestMatcher}
|
* @return the object that is chained after creating the {@link RequestMatcher}
|
||||||
|
* @deprecated use {@link #requestMatchers(RequestMatcher...)} with a
|
||||||
|
* {@link RegexRequestMatcher} instead
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public C regexMatchers(HttpMethod method, String... regexPatterns) {
|
public C regexMatchers(HttpMethod method, String... regexPatterns) {
|
||||||
Assert.state(!this.anyRequestConfigured, "Can't configure regexMatchers after anyRequest");
|
Assert.state(!this.anyRequestConfigured, "Can't configure regexMatchers after anyRequest");
|
||||||
return chainRequestMatchers(RequestMatchers.regexMatchers(method, regexPatterns));
|
return chainRequestMatchers(RequestMatchers.regexMatchers(method, regexPatterns));
|
||||||
|
@ -203,7 +226,10 @@ public abstract class AbstractRequestMatcherRegistry<C> {
|
||||||
* @param regexPatterns the regular expressions to create
|
* @param regexPatterns the regular expressions to create
|
||||||
* {@link org.springframework.security.web.util.matcher.RegexRequestMatcher} from
|
* {@link org.springframework.security.web.util.matcher.RegexRequestMatcher} from
|
||||||
* @return the object that is chained after creating the {@link RequestMatcher}
|
* @return the object that is chained after creating the {@link RequestMatcher}
|
||||||
|
* @deprecated use {@link #requestMatchers(RequestMatcher...)} with a
|
||||||
|
* {@link RegexRequestMatcher} instead
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public C regexMatchers(String... regexPatterns) {
|
public C regexMatchers(String... regexPatterns) {
|
||||||
Assert.state(!this.anyRequestConfigured, "Can't configure regexMatchers after anyRequest");
|
Assert.state(!this.anyRequestConfigured, "Can't configure regexMatchers after anyRequest");
|
||||||
return chainRequestMatchers(RequestMatchers.regexMatchers(regexPatterns));
|
return chainRequestMatchers(RequestMatchers.regexMatchers(regexPatterns));
|
||||||
|
@ -250,6 +276,81 @@ public abstract class AbstractRequestMatcherRegistry<C> {
|
||||||
return chainRequestMatchers(Arrays.asList(requestMatchers));
|
return chainRequestMatchers(Arrays.asList(requestMatchers));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* If the {@link HandlerMappingIntrospector} is available in the classpath, maps to an
|
||||||
|
* {@link MvcRequestMatcher} that also specifies a specific {@link HttpMethod} to
|
||||||
|
* match on. This matcher will use the same rules that Spring MVC uses for matching.
|
||||||
|
* For example, often times a mapping of the path "/path" will match on "/path",
|
||||||
|
* "/path/", "/path.html", etc. If the {@link HandlerMappingIntrospector} is not
|
||||||
|
* available, maps to an {@link AntPathRequestMatcher}.
|
||||||
|
* </p>
|
||||||
|
* <p>
|
||||||
|
* If a specific {@link RequestMatcher} must be specified, use
|
||||||
|
* {@link #requestMatchers(RequestMatcher...)} instead
|
||||||
|
* </p>
|
||||||
|
* @param method the {@link HttpMethod} to use or {@code null} for any
|
||||||
|
* {@link HttpMethod}.
|
||||||
|
* @param patterns the patterns to match on. The rules for matching are defined by
|
||||||
|
* Spring MVC if {@link MvcRequestMatcher} is used
|
||||||
|
* @return the object that is chained after creating the {@link RequestMatcher}.
|
||||||
|
* @since 5.8
|
||||||
|
*/
|
||||||
|
public C requestMatchers(HttpMethod method, String... patterns) {
|
||||||
|
List<RequestMatcher> matchers = new ArrayList<>();
|
||||||
|
if (mvcPresent) {
|
||||||
|
matchers.addAll(createMvcMatchers(method, patterns));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
matchers.addAll(RequestMatchers.antMatchers(method, patterns));
|
||||||
|
}
|
||||||
|
return requestMatchers(matchers.toArray(new RequestMatcher[0]));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* If the {@link HandlerMappingIntrospector} is available in the classpath, maps to an
|
||||||
|
* {@link MvcRequestMatcher} that does not care which {@link HttpMethod} is used. This
|
||||||
|
* matcher will use the same rules that Spring MVC uses for matching. For example,
|
||||||
|
* often times a mapping of the path "/path" will match on "/path", "/path/",
|
||||||
|
* "/path.html", etc. If the {@link HandlerMappingIntrospector} is not available, maps
|
||||||
|
* to an {@link AntPathRequestMatcher}.
|
||||||
|
* </p>
|
||||||
|
* <p>
|
||||||
|
* If a specific {@link RequestMatcher} must be specified, use
|
||||||
|
* {@link #requestMatchers(RequestMatcher...)} instead
|
||||||
|
* </p>
|
||||||
|
* @param patterns the patterns to match on. The rules for matching are defined by
|
||||||
|
* Spring MVC if {@link MvcRequestMatcher} is used
|
||||||
|
* @return the object that is chained after creating the {@link RequestMatcher}.
|
||||||
|
* @since 5.8
|
||||||
|
*/
|
||||||
|
public C requestMatchers(String... patterns) {
|
||||||
|
return requestMatchers(null, patterns);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* If the {@link HandlerMappingIntrospector} is available in the classpath, maps to an
|
||||||
|
* {@link MvcRequestMatcher} that matches on a specific {@link HttpMethod}. This
|
||||||
|
* matcher will use the same rules that Spring MVC uses for matching. For example,
|
||||||
|
* often times a mapping of the path "/path" will match on "/path", "/path/",
|
||||||
|
* "/path.html", etc. If the {@link HandlerMappingIntrospector} is not available, maps
|
||||||
|
* to an {@link AntPathRequestMatcher}.
|
||||||
|
* </p>
|
||||||
|
* <p>
|
||||||
|
* If a specific {@link RequestMatcher} must be specified, use
|
||||||
|
* {@link #requestMatchers(RequestMatcher...)} instead
|
||||||
|
* </p>
|
||||||
|
* @param method the {@link HttpMethod} to use or {@code null} for any
|
||||||
|
* {@link HttpMethod}.
|
||||||
|
* @return the object that is chained after creating the {@link RequestMatcher}.
|
||||||
|
* @since 5.8
|
||||||
|
*/
|
||||||
|
public C requestMatchers(HttpMethod method) {
|
||||||
|
return requestMatchers(method, "/**");
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Subclasses should implement this method for returning the object that is chained to
|
* Subclasses should implement this method for returning the object that is chained to
|
||||||
* the creation of the {@link RequestMatcher} instances.
|
* the creation of the {@link RequestMatcher} instances.
|
||||||
|
|
|
@ -28,6 +28,7 @@ import jakarta.servlet.ServletRequest;
|
||||||
import jakarta.servlet.ServletResponse;
|
import jakarta.servlet.ServletResponse;
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||||
import org.springframework.context.ApplicationContext;
|
import org.springframework.context.ApplicationContext;
|
||||||
import org.springframework.core.OrderComparator;
|
import org.springframework.core.OrderComparator;
|
||||||
import org.springframework.core.Ordered;
|
import org.springframework.core.Ordered;
|
||||||
|
@ -91,6 +92,7 @@ import org.springframework.security.web.util.matcher.OrRequestMatcher;
|
||||||
import org.springframework.security.web.util.matcher.RegexRequestMatcher;
|
import org.springframework.security.web.util.matcher.RegexRequestMatcher;
|
||||||
import org.springframework.security.web.util.matcher.RequestMatcher;
|
import org.springframework.security.web.util.matcher.RequestMatcher;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
|
import org.springframework.util.ClassUtils;
|
||||||
import org.springframework.web.cors.CorsConfiguration;
|
import org.springframework.web.cors.CorsConfiguration;
|
||||||
import org.springframework.web.filter.CorsFilter;
|
import org.springframework.web.filter.CorsFilter;
|
||||||
import org.springframework.web.servlet.handler.HandlerMappingIntrospector;
|
import org.springframework.web.servlet.handler.HandlerMappingIntrospector;
|
||||||
|
@ -116,7 +118,7 @@ import org.springframework.web.servlet.handler.HandlerMappingIntrospector;
|
||||||
*
|
*
|
||||||
* @Bean
|
* @Bean
|
||||||
* public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
|
* public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
|
||||||
* http.authorizeRequests().antMatchers("/**").hasRole("USER").and().formLogin();
|
* http.authorizeHttpRequests().requestMatchers("/**").hasRole("USER").and().formLogin();
|
||||||
* return http.build();
|
* return http.build();
|
||||||
* }
|
* }
|
||||||
*
|
*
|
||||||
|
@ -140,6 +142,12 @@ import org.springframework.web.servlet.handler.HandlerMappingIntrospector;
|
||||||
public final class HttpSecurity extends AbstractConfiguredSecurityBuilder<DefaultSecurityFilterChain, HttpSecurity>
|
public final class HttpSecurity extends AbstractConfiguredSecurityBuilder<DefaultSecurityFilterChain, HttpSecurity>
|
||||||
implements SecurityBuilder<DefaultSecurityFilterChain>, HttpSecurityBuilder<HttpSecurity> {
|
implements SecurityBuilder<DefaultSecurityFilterChain>, HttpSecurityBuilder<HttpSecurity> {
|
||||||
|
|
||||||
|
private static final String HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME = "mvcHandlerMappingIntrospector";
|
||||||
|
|
||||||
|
private static final String HANDLER_MAPPING_INTROSPECTOR = "org.springframework.web.servlet.handler.HandlerMappingIntrospector";
|
||||||
|
|
||||||
|
private static final boolean mvcPresent;
|
||||||
|
|
||||||
private final RequestMatcherConfigurer requestMatcherConfigurer;
|
private final RequestMatcherConfigurer requestMatcherConfigurer;
|
||||||
|
|
||||||
private List<OrderedFilter> filters = new ArrayList<>();
|
private List<OrderedFilter> filters = new ArrayList<>();
|
||||||
|
@ -150,6 +158,10 @@ public final class HttpSecurity extends AbstractConfiguredSecurityBuilder<Defaul
|
||||||
|
|
||||||
private AuthenticationManager authenticationManager;
|
private AuthenticationManager authenticationManager;
|
||||||
|
|
||||||
|
static {
|
||||||
|
mvcPresent = ClassUtils.isPresent(HANDLER_MAPPING_INTROSPECTOR, HttpSecurity.class.getClassLoader());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new instance
|
* Creates a new instance
|
||||||
* @param objectPostProcessor the {@link ObjectPostProcessor} that should be used
|
* @param objectPostProcessor the {@link ObjectPostProcessor} that should be used
|
||||||
|
@ -3193,7 +3205,9 @@ public final class HttpSecurity extends AbstractConfiguredSecurityBuilder<Defaul
|
||||||
* }
|
* }
|
||||||
* </pre>
|
* </pre>
|
||||||
* @return the {@link RequestMatcherConfigurer} for further customizations
|
* @return the {@link RequestMatcherConfigurer} for further customizations
|
||||||
|
* @deprecated use {@link #securityMatchers()} instead
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public RequestMatcherConfigurer requestMatchers() {
|
public RequestMatcherConfigurer requestMatchers() {
|
||||||
return this.requestMatcherConfigurer;
|
return this.requestMatcherConfigurer;
|
||||||
}
|
}
|
||||||
|
@ -3325,7 +3339,9 @@ public final class HttpSecurity extends AbstractConfiguredSecurityBuilder<Defaul
|
||||||
* @param requestMatcherCustomizer the {@link Customizer} to provide more options for
|
* @param requestMatcherCustomizer the {@link Customizer} to provide more options for
|
||||||
* the {@link RequestMatcherConfigurer}
|
* the {@link RequestMatcherConfigurer}
|
||||||
* @return the {@link HttpSecurity} for further customizations
|
* @return the {@link HttpSecurity} for further customizations
|
||||||
|
* @deprecated use {@link #securityMatchers(Customizer)} instead
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public HttpSecurity requestMatchers(Customizer<RequestMatcherConfigurer> requestMatcherCustomizer) {
|
public HttpSecurity requestMatchers(Customizer<RequestMatcherConfigurer> requestMatcherCustomizer) {
|
||||||
requestMatcherCustomizer.customize(this.requestMatcherConfigurer);
|
requestMatcherCustomizer.customize(this.requestMatcherConfigurer);
|
||||||
return HttpSecurity.this;
|
return HttpSecurity.this;
|
||||||
|
@ -3345,15 +3361,318 @@ public final class HttpSecurity extends AbstractConfiguredSecurityBuilder<Defaul
|
||||||
* @param requestMatcher the {@link RequestMatcher} to use (i.e. new
|
* @param requestMatcher the {@link RequestMatcher} to use (i.e. new
|
||||||
* AntPathRequestMatcher("/admin/**","GET") )
|
* AntPathRequestMatcher("/admin/**","GET") )
|
||||||
* @return the {@link HttpSecurity} for further customizations
|
* @return the {@link HttpSecurity} for further customizations
|
||||||
|
* @deprecated use {@link #securityMatcher(RequestMatcher)} instead
|
||||||
* @see #requestMatchers()
|
* @see #requestMatchers()
|
||||||
* @see #antMatcher(String)
|
* @see #antMatcher(String)
|
||||||
* @see #regexMatcher(String)
|
* @see #regexMatcher(String)
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public HttpSecurity requestMatcher(RequestMatcher requestMatcher) {
|
public HttpSecurity requestMatcher(RequestMatcher requestMatcher) {
|
||||||
this.requestMatcher = requestMatcher;
|
this.requestMatcher = requestMatcher;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 #securityMatcher(String)}, or {@link #securityMatcher(RequestMatcher)}.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Invoking {@link #securityMatchers()} will not override previous invocations of
|
||||||
|
* {@link #securityMatchers()}}, {@link #securityMatchers(Customizer)}
|
||||||
|
* {@link #securityMatcher(String)} and {@link #securityMatcher(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 {
|
||||||
|
*
|
||||||
|
* @Bean
|
||||||
|
* public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
|
||||||
|
* http
|
||||||
|
* .securityMatchers((matchers) -> matchers
|
||||||
|
* .requestMatchers("/api/**", "/oauth/**")
|
||||||
|
* )
|
||||||
|
* .authorizeHttpRequests((authorize) -> authorize
|
||||||
|
* anyRequest().hasRole("USER")
|
||||||
|
* )
|
||||||
|
* .httpBasic(withDefaults());
|
||||||
|
* return http.build();
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* @Bean
|
||||||
|
* public UserDetailsService userDetailsService() {
|
||||||
|
* UserDetails user = User.withDefaultPasswordEncoder()
|
||||||
|
* .username("user")
|
||||||
|
* .password("password")
|
||||||
|
* .roles("USER")
|
||||||
|
* .build();
|
||||||
|
* return new InMemoryUserDetailsManager(user);
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* The configuration below is the same as the previous configuration.
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* @Configuration
|
||||||
|
* @EnableWebSecurity
|
||||||
|
* public class RequestMatchersSecurityConfig {
|
||||||
|
*
|
||||||
|
* @Bean
|
||||||
|
* public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
|
||||||
|
* http
|
||||||
|
* .securityMatchers((matchers) -> matchers
|
||||||
|
* .requestMatchers("/api/**")
|
||||||
|
* .requestMatchers("/oauth/**")
|
||||||
|
* )
|
||||||
|
* .authorizeHttpRequests((authorize) -> authorize
|
||||||
|
* anyRequest().hasRole("USER")
|
||||||
|
* )
|
||||||
|
* .httpBasic(withDefaults());
|
||||||
|
* return http.build();
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* @Bean
|
||||||
|
* public UserDetailsService userDetailsService() {
|
||||||
|
* UserDetails user = User.withDefaultPasswordEncoder()
|
||||||
|
* .username("user")
|
||||||
|
* .password("password")
|
||||||
|
* .roles("USER")
|
||||||
|
* .build();
|
||||||
|
* return new InMemoryUserDetailsManager(user);
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* The configuration below is also the same as the above configuration.
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* @Configuration
|
||||||
|
* @EnableWebSecurity
|
||||||
|
* public class RequestMatchersSecurityConfig {
|
||||||
|
*
|
||||||
|
* @Bean
|
||||||
|
* public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
|
||||||
|
* http
|
||||||
|
* .securityMatchers((matchers) -> matchers
|
||||||
|
* .requestMatchers("/api/**")
|
||||||
|
* )
|
||||||
|
* .securityMatchers((matchers) -> matchers
|
||||||
|
* .requestMatchers("/oauth/**")
|
||||||
|
* )
|
||||||
|
* .authorizeHttpRequests((authorize) -> authorize
|
||||||
|
* anyRequest().hasRole("USER")
|
||||||
|
* )
|
||||||
|
* .httpBasic(withDefaults());
|
||||||
|
* return http.build();
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* @Bean
|
||||||
|
* public UserDetailsService userDetailsService() {
|
||||||
|
* UserDetails user = User.withDefaultPasswordEncoder()
|
||||||
|
* .username("user")
|
||||||
|
* .password("password")
|
||||||
|
* .roles("USER")
|
||||||
|
* .build();
|
||||||
|
* return new InMemoryUserDetailsManager(user);
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* </pre>
|
||||||
|
* @return the {@link RequestMatcherConfigurer} for further customizations
|
||||||
|
*/
|
||||||
|
public RequestMatcherConfigurer securityMatchers() {
|
||||||
|
return this.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 #securityMatcher(String)}, or {@link #securityMatcher(RequestMatcher)}.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Invoking {@link #securityMatchers(Customizer)} will not override previous
|
||||||
|
* invocations of {@link #securityMatchers()}}, {@link #securityMatchers(Customizer)}
|
||||||
|
* {@link #securityMatcher(String)} and {@link #securityMatcher(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 {
|
||||||
|
*
|
||||||
|
* @Bean
|
||||||
|
* public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
|
||||||
|
* http
|
||||||
|
* .securityMatchers((matchers) -> matchers
|
||||||
|
* .requestMatchers("/api/**", "/oauth/**")
|
||||||
|
* )
|
||||||
|
* .authorizeHttpRequests((authorize) -> authorize
|
||||||
|
* .anyRequest().hasRole("USER")
|
||||||
|
* )
|
||||||
|
* .httpBasic(withDefaults());
|
||||||
|
* return http.build();
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* @Bean
|
||||||
|
* public UserDetailsService userDetailsService() {
|
||||||
|
* UserDetails user = User.withDefaultPasswordEncoder()
|
||||||
|
* .username("user")
|
||||||
|
* .password("password")
|
||||||
|
* .roles("USER")
|
||||||
|
* .build();
|
||||||
|
* return new InMemoryUserDetailsManager(user);
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* The configuration below is the same as the previous configuration.
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* @Configuration
|
||||||
|
* @EnableWebSecurity
|
||||||
|
* public class RequestMatchersSecurityConfig {
|
||||||
|
*
|
||||||
|
* @Bean
|
||||||
|
* public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
|
||||||
|
* http
|
||||||
|
* .securityMatchers((matchers) -> matchers
|
||||||
|
* .requestMatchers("/api/**")
|
||||||
|
* .requestMatchers("/oauth/**")
|
||||||
|
* )
|
||||||
|
* .authorizeHttpRequests((authorize) -> authorize
|
||||||
|
* .anyRequest().hasRole("USER")
|
||||||
|
* )
|
||||||
|
* .httpBasic(withDefaults());
|
||||||
|
* return http.build();
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* @Bean
|
||||||
|
* public UserDetailsService userDetailsService() {
|
||||||
|
* UserDetails user = User.withDefaultPasswordEncoder()
|
||||||
|
* .username("user")
|
||||||
|
* .password("password")
|
||||||
|
* .roles("USER")
|
||||||
|
* .build();
|
||||||
|
* return new InMemoryUserDetailsManager(user);
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* The configuration below is also the same as the above configuration.
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* @Configuration
|
||||||
|
* @EnableWebSecurity
|
||||||
|
* public class RequestMatchersSecurityConfig {
|
||||||
|
*
|
||||||
|
* @Bean
|
||||||
|
* public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
|
||||||
|
* http
|
||||||
|
* .securityMatchers((matchers) -> matchers
|
||||||
|
* .requestMatchers("/api/**")
|
||||||
|
* )
|
||||||
|
* .securityMatchers((matchers) -> matchers
|
||||||
|
* .requestMatchers("/oauth/**")
|
||||||
|
* )
|
||||||
|
* .authorizeHttpRequests((authorize) -> authorize
|
||||||
|
* .anyRequest().hasRole("USER")
|
||||||
|
* )
|
||||||
|
* .httpBasic(withDefaults());
|
||||||
|
* return http.build();
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* @Bean
|
||||||
|
* public UserDetailsService userDetailsService() {
|
||||||
|
* UserDetails user = User.withDefaultPasswordEncoder()
|
||||||
|
* .username("user")
|
||||||
|
* .password("password")
|
||||||
|
* .roles("USER")
|
||||||
|
* .build();
|
||||||
|
* return new InMemoryUserDetailsManager(user);
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* </pre>
|
||||||
|
* @param requestMatcherCustomizer the {@link Customizer} to provide more options for
|
||||||
|
* the {@link RequestMatcherConfigurer}
|
||||||
|
* @return the {@link HttpSecurity} for further customizations
|
||||||
|
*/
|
||||||
|
public HttpSecurity securityMatchers(Customizer<RequestMatcherConfigurer> requestMatcherCustomizer) {
|
||||||
|
requestMatcherCustomizer.customize(this.requestMatcherConfigurer);
|
||||||
|
return HttpSecurity.this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allows configuring the {@link HttpSecurity} to only be invoked when matching the
|
||||||
|
* provided {@link RequestMatcher}. If more advanced configuration is necessary,
|
||||||
|
* consider using {@link #securityMatchers(Customizer)} ()}.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Invoking {@link #securityMatcher(RequestMatcher)} will override previous
|
||||||
|
* invocations of {@link #requestMatchers()}, {@link #mvcMatcher(String)},
|
||||||
|
* {@link #antMatcher(String)}, {@link #regexMatcher(String)},
|
||||||
|
* {@link #requestMatcher(RequestMatcher)}, {@link #securityMatchers(Customizer)},
|
||||||
|
* {@link #securityMatchers()} and {@link #securityMatcher(String)}
|
||||||
|
* </p>
|
||||||
|
* @param requestMatcher the {@link RequestMatcher} to use (i.e. new
|
||||||
|
* AntPathRequestMatcher("/admin/**","GET") )
|
||||||
|
* @return the {@link HttpSecurity} for further customizations
|
||||||
|
* @see #securityMatcher(String)
|
||||||
|
*/
|
||||||
|
public HttpSecurity securityMatcher(RequestMatcher requestMatcher) {
|
||||||
|
this.requestMatcher = requestMatcher;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allows configuring the {@link HttpSecurity} to only be invoked when matching the
|
||||||
|
* provided pattern. This method creates a {@link MvcRequestMatcher} if Spring MVC is
|
||||||
|
* in the classpath or creates an {@link AntPathRequestMatcher} if not. If more
|
||||||
|
* advanced configuration is necessary, consider using
|
||||||
|
* {@link #securityMatchers(Customizer)} or {@link #securityMatcher(RequestMatcher)}.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Invoking {@link #securityMatcher(String)} will override previous invocations of
|
||||||
|
* {@link #mvcMatcher(String)}}, {@link #requestMatchers()},
|
||||||
|
* {@link #antMatcher(String)}, {@link #regexMatcher(String)}, and
|
||||||
|
* {@link #requestMatcher(RequestMatcher)}.
|
||||||
|
* </p>
|
||||||
|
* @param pattern the pattern to match on (i.e. "/admin/**")
|
||||||
|
* @return the {@link HttpSecurity} for further customizations
|
||||||
|
* @see AntPathRequestMatcher
|
||||||
|
* @see MvcRequestMatcher
|
||||||
|
*/
|
||||||
|
public HttpSecurity securityMatcher(String pattern) {
|
||||||
|
if (!mvcPresent) {
|
||||||
|
this.requestMatcher = new AntPathRequestMatcher(pattern);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
if (!getContext().containsBean(HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME)) {
|
||||||
|
throw new NoSuchBeanDefinitionException("A Bean named " + HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME
|
||||||
|
+ " of type " + HandlerMappingIntrospector.class.getName()
|
||||||
|
+ " is required to use MvcRequestMatcher. Please ensure Spring Security & Spring MVC are configured in a shared ApplicationContext.");
|
||||||
|
}
|
||||||
|
HandlerMappingIntrospector introspector = getContext().getBean(HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME,
|
||||||
|
HandlerMappingIntrospector.class);
|
||||||
|
this.requestMatcher = new MvcRequestMatcher(introspector, pattern);
|
||||||
|
return 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 ant pattern. If more advanced configuration is necessary, consider using
|
* provided ant pattern. If more advanced configuration is necessary, consider using
|
||||||
|
@ -3367,8 +3686,10 @@ public final class HttpSecurity extends AbstractConfiguredSecurityBuilder<Defaul
|
||||||
* </p>
|
* </p>
|
||||||
* @param antPattern the Ant Pattern to match on (i.e. "/admin/**")
|
* @param antPattern the Ant Pattern to match on (i.e. "/admin/**")
|
||||||
* @return the {@link HttpSecurity} for further customizations
|
* @return the {@link HttpSecurity} for further customizations
|
||||||
|
* @deprecated use {@link #securityMatcher(String)} instead
|
||||||
* @see AntPathRequestMatcher
|
* @see AntPathRequestMatcher
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public HttpSecurity antMatcher(String antPattern) {
|
public HttpSecurity antMatcher(String antPattern) {
|
||||||
return requestMatcher(new AntPathRequestMatcher(antPattern));
|
return requestMatcher(new AntPathRequestMatcher(antPattern));
|
||||||
}
|
}
|
||||||
|
@ -3386,8 +3707,10 @@ public final class HttpSecurity extends AbstractConfiguredSecurityBuilder<Defaul
|
||||||
* </p>
|
* </p>
|
||||||
* @param mvcPattern the Spring MVC Pattern to match on (i.e. "/admin/**")
|
* @param mvcPattern the Spring MVC Pattern to match on (i.e. "/admin/**")
|
||||||
* @return the {@link HttpSecurity} for further customizations
|
* @return the {@link HttpSecurity} for further customizations
|
||||||
|
* @deprecated use {@link #securityMatcher(String)} instead
|
||||||
* @see MvcRequestMatcher
|
* @see MvcRequestMatcher
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public HttpSecurity mvcMatcher(String mvcPattern) {
|
public HttpSecurity mvcMatcher(String mvcPattern) {
|
||||||
HandlerMappingIntrospector introspector = new HandlerMappingIntrospector();
|
HandlerMappingIntrospector introspector = new HandlerMappingIntrospector();
|
||||||
introspector.setApplicationContext(getContext());
|
introspector.setApplicationContext(getContext());
|
||||||
|
@ -3408,8 +3731,10 @@ public final class HttpSecurity extends AbstractConfiguredSecurityBuilder<Defaul
|
||||||
* </p>
|
* </p>
|
||||||
* @param pattern the Regular Expression to match on (i.e. "/admin/.+")
|
* @param pattern the Regular Expression to match on (i.e. "/admin/.+")
|
||||||
* @return the {@link HttpSecurity} for further customizations
|
* @return the {@link HttpSecurity} for further customizations
|
||||||
* @see RegexRequestMatcher
|
* @deprecated use {@link #securityMatcher(RequestMatcher)} with a
|
||||||
|
* {@link RegexRequestMatcher} instead
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public HttpSecurity regexMatcher(String pattern) {
|
public HttpSecurity regexMatcher(String pattern) {
|
||||||
return requestMatcher(new RegexRequestMatcher(pattern, null));
|
return requestMatcher(new RegexRequestMatcher(pattern, null));
|
||||||
}
|
}
|
||||||
|
@ -3480,14 +3805,22 @@ public final class HttpSecurity extends AbstractConfiguredSecurityBuilder<Defaul
|
||||||
setApplicationContext(context);
|
setApplicationContext(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated use {@link #requestMatchers(HttpMethod, String...)} instead
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
|
@Deprecated
|
||||||
public MvcMatchersRequestMatcherConfigurer mvcMatchers(HttpMethod method, String... mvcPatterns) {
|
public MvcMatchersRequestMatcherConfigurer mvcMatchers(HttpMethod method, String... mvcPatterns) {
|
||||||
List<MvcRequestMatcher> mvcMatchers = createMvcMatchers(method, mvcPatterns);
|
List<MvcRequestMatcher> mvcMatchers = createMvcMatchers(method, mvcPatterns);
|
||||||
setMatchers(mvcMatchers);
|
setMatchers(mvcMatchers);
|
||||||
return new MvcMatchersRequestMatcherConfigurer(getContext(), mvcMatchers, this.matchers);
|
return new MvcMatchersRequestMatcherConfigurer(getContext(), mvcMatchers, this.matchers);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated use {@link #requestMatchers(String...)} instead
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
|
@Deprecated
|
||||||
public MvcMatchersRequestMatcherConfigurer mvcMatchers(String... patterns) {
|
public MvcMatchersRequestMatcherConfigurer mvcMatchers(String... patterns) {
|
||||||
return mvcMatchers(null, patterns);
|
return mvcMatchers(null, patterns);
|
||||||
}
|
}
|
||||||
|
@ -3500,7 +3833,7 @@ public final class HttpSecurity extends AbstractConfiguredSecurityBuilder<Defaul
|
||||||
|
|
||||||
private void setMatchers(List<? extends RequestMatcher> requestMatchers) {
|
private void setMatchers(List<? extends RequestMatcher> requestMatchers) {
|
||||||
this.matchers.addAll(requestMatchers);
|
this.matchers.addAll(requestMatchers);
|
||||||
requestMatcher(new OrRequestMatcher(this.matchers));
|
securityMatcher(new OrRequestMatcher(this.matchers));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -135,7 +135,7 @@ public final class WebSecurity extends AbstractConfiguredSecurityBuilder<Filter,
|
||||||
* <pre>
|
* <pre>
|
||||||
* webSecurityBuilder.ignoring()
|
* webSecurityBuilder.ignoring()
|
||||||
* // ignore all URLs that start with /resources/ or /static/
|
* // ignore all URLs that start with /resources/ or /static/
|
||||||
* .antMatchers("/resources/**", "/static/**");
|
* .requestMatchers("/resources/**", "/static/**");
|
||||||
* </pre>
|
* </pre>
|
||||||
*
|
*
|
||||||
* Alternatively this will accomplish the same result:
|
* Alternatively this will accomplish the same result:
|
||||||
|
@ -143,7 +143,7 @@ public final class WebSecurity extends AbstractConfiguredSecurityBuilder<Filter,
|
||||||
* <pre>
|
* <pre>
|
||||||
* webSecurityBuilder.ignoring()
|
* webSecurityBuilder.ignoring()
|
||||||
* // ignore all URLs that start with /resources/ or /static/
|
* // ignore all URLs that start with /resources/ or /static/
|
||||||
* .antMatchers("/resources/**").antMatchers("/static/**");
|
* .requestMatchers("/resources/**").requestMatchers("/static/**");
|
||||||
* </pre>
|
* </pre>
|
||||||
*
|
*
|
||||||
* Multiple invocations of ignoring() are also additive, so the following is also
|
* Multiple invocations of ignoring() are also additive, so the following is also
|
||||||
|
@ -152,10 +152,10 @@ public final class WebSecurity extends AbstractConfiguredSecurityBuilder<Filter,
|
||||||
* <pre>
|
* <pre>
|
||||||
* webSecurityBuilder.ignoring()
|
* webSecurityBuilder.ignoring()
|
||||||
* // ignore all URLs that start with /resources/
|
* // ignore all URLs that start with /resources/
|
||||||
* .antMatchers("/resources/**");
|
* .requestMatchers("/resources/**");
|
||||||
* webSecurityBuilder.ignoring()
|
* webSecurityBuilder.ignoring()
|
||||||
* // ignore all URLs that start with /static/
|
* // ignore all URLs that start with /static/
|
||||||
* .antMatchers("/static/**");
|
* .requestMatchers("/static/**");
|
||||||
* // now both URLs that start with /resources/ and /static/ will be ignored
|
* // now both URLs that start with /resources/ and /static/ will be ignored
|
||||||
* </pre>
|
* </pre>
|
||||||
* @return the {@link IgnoredRequestConfigurer} to use for registering request that
|
* @return the {@link IgnoredRequestConfigurer} to use for registering request that
|
||||||
|
@ -380,7 +380,9 @@ public final class WebSecurity extends AbstractConfiguredSecurityBuilder<Filter,
|
||||||
* {@link MvcRequestMatcher#setMethod(HttpMethod)}
|
* {@link MvcRequestMatcher#setMethod(HttpMethod)}
|
||||||
*
|
*
|
||||||
* @author Rob Winch
|
* @author Rob Winch
|
||||||
|
* @deprecated use {@link MvcRequestMatcher.Builder} instead
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public final class MvcMatchersIgnoredRequestConfigurer extends IgnoredRequestConfigurer {
|
public final class MvcMatchersIgnoredRequestConfigurer extends IgnoredRequestConfigurer {
|
||||||
|
|
||||||
private final List<MvcRequestMatcher> mvcMatchers;
|
private final List<MvcRequestMatcher> mvcMatchers;
|
||||||
|
@ -412,14 +414,22 @@ public final class WebSecurity extends AbstractConfiguredSecurityBuilder<Filter,
|
||||||
setApplicationContext(context);
|
setApplicationContext(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated use {@link #requestMatchers(HttpMethod, String...)} instead
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
|
@Deprecated
|
||||||
public MvcMatchersIgnoredRequestConfigurer mvcMatchers(HttpMethod method, String... mvcPatterns) {
|
public MvcMatchersIgnoredRequestConfigurer mvcMatchers(HttpMethod method, String... mvcPatterns) {
|
||||||
List<MvcRequestMatcher> mvcMatchers = createMvcMatchers(method, mvcPatterns);
|
List<MvcRequestMatcher> mvcMatchers = createMvcMatchers(method, mvcPatterns);
|
||||||
WebSecurity.this.ignoredRequests.addAll(mvcMatchers);
|
WebSecurity.this.ignoredRequests.addAll(mvcMatchers);
|
||||||
return new MvcMatchersIgnoredRequestConfigurer(getApplicationContext(), mvcMatchers);
|
return new MvcMatchersIgnoredRequestConfigurer(getApplicationContext(), mvcMatchers);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated use {@link #requestMatchers(String...)} instead
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
|
@Deprecated
|
||||||
public MvcMatchersIgnoredRequestConfigurer mvcMatchers(String... mvcPatterns) {
|
public MvcMatchersIgnoredRequestConfigurer mvcMatchers(String... mvcPatterns) {
|
||||||
return mvcMatchers(null, mvcPatterns);
|
return mvcMatchers(null, mvcPatterns);
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,12 +41,12 @@ import org.springframework.security.web.SecurityFilterChain;
|
||||||
* public WebSecurityCustomizer webSecurityCustomizer() {
|
* public WebSecurityCustomizer webSecurityCustomizer() {
|
||||||
* return (web) -> web.ignoring()
|
* return (web) -> web.ignoring()
|
||||||
* // Spring Security should completely ignore URLs starting with /resources/
|
* // Spring Security should completely ignore URLs starting with /resources/
|
||||||
* .antMatchers("/resources/**");
|
* .requestMatchers("/resources/**");
|
||||||
* }
|
* }
|
||||||
*
|
*
|
||||||
* @Bean
|
* @Bean
|
||||||
* public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
|
* public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
|
||||||
* http.authorizeRequests().antMatchers("/public/**").permitAll().anyRequest()
|
* http.authorizeRequests().requestMatchers("/public/**").permitAll().anyRequest()
|
||||||
* .hasRole("USER").and()
|
* .hasRole("USER").and()
|
||||||
* // Possibly more configuration ...
|
* // Possibly more configuration ...
|
||||||
* .formLogin() // enable form based log in
|
* .formLogin() // enable form based log in
|
||||||
|
|
|
@ -146,12 +146,20 @@ public final class AuthorizeHttpRequestsConfigurer<H extends HttpSecurityBuilder
|
||||||
return postProcess(this.managerBuilder.build());
|
return postProcess(this.managerBuilder.build());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated use {@link #requestMatchers(String...)} instead
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
|
@Deprecated
|
||||||
public MvcMatchersAuthorizedUrl mvcMatchers(String... mvcPatterns) {
|
public MvcMatchersAuthorizedUrl mvcMatchers(String... mvcPatterns) {
|
||||||
return mvcMatchers(null, mvcPatterns);
|
return mvcMatchers(null, mvcPatterns);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated use {@link #requestMatchers(HttpMethod, String...)} instead
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
|
@Deprecated
|
||||||
public MvcMatchersAuthorizedUrl mvcMatchers(HttpMethod method, String... mvcPatterns) {
|
public MvcMatchersAuthorizedUrl mvcMatchers(HttpMethod method, String... mvcPatterns) {
|
||||||
return new MvcMatchersAuthorizedUrl(createMvcMatchers(method, mvcPatterns));
|
return new MvcMatchersAuthorizedUrl(createMvcMatchers(method, mvcPatterns));
|
||||||
}
|
}
|
||||||
|
@ -202,7 +210,9 @@ public final class AuthorizeHttpRequestsConfigurer<H extends HttpSecurityBuilder
|
||||||
* configuring the {@link MvcRequestMatcher#setServletPath(String)}.
|
* configuring the {@link MvcRequestMatcher#setServletPath(String)}.
|
||||||
*
|
*
|
||||||
* @author Evgeniy Cheban
|
* @author Evgeniy Cheban
|
||||||
|
* @deprecated use {@link MvcRequestMatcher.Builder} instead
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public final class MvcMatchersAuthorizedUrl extends AuthorizedUrl {
|
public final class MvcMatchersAuthorizedUrl extends AuthorizedUrl {
|
||||||
|
|
||||||
private MvcMatchersAuthorizedUrl(List<MvcRequestMatcher> matchers) {
|
private MvcMatchersAuthorizedUrl(List<MvcRequestMatcher> matchers) {
|
||||||
|
|
|
@ -163,7 +163,10 @@ public final class CsrfConfigurer<H extends HttpSecurityBuilder<H>>
|
||||||
* </pre>
|
* </pre>
|
||||||
*
|
*
|
||||||
* @since 4.0
|
* @since 4.0
|
||||||
|
* @deprecated use {@link #ignoringRequestMatchers(RequestMatcher...)} with an
|
||||||
|
* {@link org.springframework.security.web.util.matcher.AntPathRequestMatcher} instead
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public CsrfConfigurer<H> ignoringAntMatchers(String... antPatterns) {
|
public CsrfConfigurer<H> ignoringAntMatchers(String... antPatterns) {
|
||||||
return new IgnoreCsrfProtectionRegistry(this.context).antMatchers(antPatterns).and();
|
return new IgnoreCsrfProtectionRegistry(this.context).antMatchers(antPatterns).and();
|
||||||
}
|
}
|
||||||
|
@ -197,6 +200,35 @@ public final class CsrfConfigurer<H extends HttpSecurityBuilder<H>>
|
||||||
return new IgnoreCsrfProtectionRegistry(this.context).requestMatchers(requestMatchers).and();
|
return new IgnoreCsrfProtectionRegistry(this.context).requestMatchers(requestMatchers).and();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Allows specifying {@link HttpServletRequest} that should not use CSRF Protection
|
||||||
|
* even if they match the {@link #requireCsrfProtectionMatcher(RequestMatcher)}.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* For example, the following configuration will ensure CSRF protection ignores:
|
||||||
|
* </p>
|
||||||
|
* <ul>
|
||||||
|
* <li>Any GET, HEAD, TRACE, OPTIONS (this is the default)</li>
|
||||||
|
* <li>We also explicitly state to ignore any request that starts with "/sockjs/"</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* http
|
||||||
|
* .csrf()
|
||||||
|
* .ignoringRequestMatchers("/sockjs/**")
|
||||||
|
* .and()
|
||||||
|
* ...
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @since 5.8
|
||||||
|
* @see AbstractRequestMatcherRegistry#requestMatchers(String...)
|
||||||
|
*/
|
||||||
|
public CsrfConfigurer<H> ignoringRequestMatchers(String... patterns) {
|
||||||
|
return new IgnoreCsrfProtectionRegistry(this.context).requestMatchers(patterns).and();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>
|
* <p>
|
||||||
* Specify the {@link SessionAuthenticationStrategy} to use. The default is a
|
* Specify the {@link SessionAuthenticationStrategy} to use. The default is a
|
||||||
|
@ -350,14 +382,22 @@ public final class CsrfConfigurer<H extends HttpSecurityBuilder<H>>
|
||||||
setApplicationContext(context);
|
setApplicationContext(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated use {@link #requestMatchers(HttpMethod, String...)} instead
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
|
@Deprecated
|
||||||
public MvcMatchersIgnoreCsrfProtectionRegistry mvcMatchers(HttpMethod method, String... mvcPatterns) {
|
public MvcMatchersIgnoreCsrfProtectionRegistry mvcMatchers(HttpMethod method, String... mvcPatterns) {
|
||||||
List<MvcRequestMatcher> mvcMatchers = createMvcMatchers(method, mvcPatterns);
|
List<MvcRequestMatcher> mvcMatchers = createMvcMatchers(method, mvcPatterns);
|
||||||
CsrfConfigurer.this.ignoredCsrfProtectionMatchers.addAll(mvcMatchers);
|
CsrfConfigurer.this.ignoredCsrfProtectionMatchers.addAll(mvcMatchers);
|
||||||
return new MvcMatchersIgnoreCsrfProtectionRegistry(getApplicationContext(), mvcMatchers);
|
return new MvcMatchersIgnoreCsrfProtectionRegistry(getApplicationContext(), mvcMatchers);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated use {@link #requestMatchers(String...)} instead
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
|
@Deprecated
|
||||||
public MvcMatchersIgnoreCsrfProtectionRegistry mvcMatchers(String... mvcPatterns) {
|
public MvcMatchersIgnoreCsrfProtectionRegistry mvcMatchers(String... mvcPatterns) {
|
||||||
return mvcMatchers(null, mvcPatterns);
|
return mvcMatchers(null, mvcPatterns);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2020 the original author or authors.
|
* Copyright 2002-2022 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.
|
||||||
|
@ -42,6 +42,7 @@ class CsrfDsl {
|
||||||
|
|
||||||
private var ignoringAntMatchers: Array<out String>? = null
|
private var ignoringAntMatchers: Array<out String>? = null
|
||||||
private var ignoringRequestMatchers: Array<out RequestMatcher>? = null
|
private var ignoringRequestMatchers: Array<out RequestMatcher>? = null
|
||||||
|
private var ignoringRequestMatchersPatterns: Array<out String>? = null
|
||||||
private var disabled = false
|
private var disabled = false
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -66,6 +67,16 @@ class CsrfDsl {
|
||||||
ignoringRequestMatchers = requestMatchers
|
ignoringRequestMatchers = requestMatchers
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allows specifying [HttpServletRequest]s that should not use CSRF Protection
|
||||||
|
* even if they match the [requireCsrfProtectionMatcher].
|
||||||
|
*
|
||||||
|
* @param patterns the patterns that should not use CSRF protection
|
||||||
|
*/
|
||||||
|
fun ignoringRequestMatchers(vararg patterns: String) {
|
||||||
|
ignoringRequestMatchersPatterns = patterns
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Disable CSRF protection
|
* Disable CSRF protection
|
||||||
*/
|
*/
|
||||||
|
@ -80,6 +91,7 @@ class CsrfDsl {
|
||||||
sessionAuthenticationStrategy?.also { csrf.sessionAuthenticationStrategy(sessionAuthenticationStrategy) }
|
sessionAuthenticationStrategy?.also { csrf.sessionAuthenticationStrategy(sessionAuthenticationStrategy) }
|
||||||
ignoringAntMatchers?.also { csrf.ignoringAntMatchers(*ignoringAntMatchers!!) }
|
ignoringAntMatchers?.also { csrf.ignoringAntMatchers(*ignoringAntMatchers!!) }
|
||||||
ignoringRequestMatchers?.also { csrf.ignoringRequestMatchers(*ignoringRequestMatchers!!) }
|
ignoringRequestMatchers?.also { csrf.ignoringRequestMatchers(*ignoringRequestMatchers!!) }
|
||||||
|
ignoringRequestMatchersPatterns?.also { csrf.ignoringRequestMatchers(*ignoringRequestMatchersPatterns!!) }
|
||||||
if (disabled) {
|
if (disabled) {
|
||||||
csrf.disable()
|
csrf.disable()
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2018 the original author or authors.
|
* Copyright 2002-2022 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.
|
||||||
|
@ -16,19 +16,28 @@
|
||||||
|
|
||||||
package org.springframework.security.config.annotation.web;
|
package org.springframework.security.config.annotation.web;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.Modifier;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import jakarta.servlet.DispatcherType;
|
import jakarta.servlet.DispatcherType;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||||
|
import org.springframework.context.ApplicationContext;
|
||||||
import org.springframework.http.HttpMethod;
|
import org.springframework.http.HttpMethod;
|
||||||
|
import org.springframework.security.config.annotation.ObjectPostProcessor;
|
||||||
|
import org.springframework.security.web.servlet.util.matcher.MvcRequestMatcher;
|
||||||
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
|
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
|
||||||
import org.springframework.security.web.util.matcher.DispatcherTypeRequestMatcher;
|
import org.springframework.security.web.util.matcher.DispatcherTypeRequestMatcher;
|
||||||
import org.springframework.security.web.util.matcher.RegexRequestMatcher;
|
import org.springframework.security.web.util.matcher.RegexRequestMatcher;
|
||||||
import org.springframework.security.web.util.matcher.RequestMatcher;
|
import org.springframework.security.web.util.matcher.RequestMatcher;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||||
|
import static org.mockito.BDDMockito.given;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests for {@link AbstractRequestMatcherRegistry}.
|
* Tests for {@link AbstractRequestMatcherRegistry}.
|
||||||
|
@ -37,11 +46,21 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||||
*/
|
*/
|
||||||
public class AbstractRequestMatcherRegistryTests {
|
public class AbstractRequestMatcherRegistryTests {
|
||||||
|
|
||||||
|
private static final ObjectPostProcessor<Object> NO_OP_OBJECT_POST_PROCESSOR = new ObjectPostProcessor<Object>() {
|
||||||
|
@Override
|
||||||
|
public <O> O postProcess(O object) {
|
||||||
|
return object;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
private TestRequestMatcherRegistry matcherRegistry;
|
private TestRequestMatcherRegistry matcherRegistry;
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
public void setUp() {
|
public void setUp() {
|
||||||
this.matcherRegistry = new TestRequestMatcherRegistry();
|
this.matcherRegistry = new TestRequestMatcherRegistry();
|
||||||
|
ApplicationContext context = mock(ApplicationContext.class);
|
||||||
|
given(context.getBean(ObjectPostProcessor.class)).willReturn(NO_OP_OBJECT_POST_PROCESSOR);
|
||||||
|
this.matcherRegistry.setApplicationContext(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -93,6 +112,91 @@ public class AbstractRequestMatcherRegistryTests {
|
||||||
assertThat(requestMatchers.get(0)).isExactlyInstanceOf(DispatcherTypeRequestMatcher.class);
|
assertThat(requestMatchers.get(0)).isExactlyInstanceOf(DispatcherTypeRequestMatcher.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void requestMatchersWhenPatternAndMvcPresentThenReturnMvcRequestMatcherType() throws Exception {
|
||||||
|
mockMvcPresentClasspath(true);
|
||||||
|
mockMvcIntrospector(true);
|
||||||
|
List<RequestMatcher> requestMatchers = this.matcherRegistry.requestMatchers("/path");
|
||||||
|
assertThat(requestMatchers).isNotEmpty();
|
||||||
|
assertThat(requestMatchers.size()).isEqualTo(1);
|
||||||
|
assertThat(requestMatchers.get(0)).isExactlyInstanceOf(MvcRequestMatcher.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void requestMatchersWhenHttpMethodAndPatternAndMvcPresentThenReturnMvcRequestMatcherType() throws Exception {
|
||||||
|
mockMvcPresentClasspath(true);
|
||||||
|
mockMvcIntrospector(true);
|
||||||
|
List<RequestMatcher> requestMatchers = this.matcherRegistry.requestMatchers(HttpMethod.GET, "/path");
|
||||||
|
assertThat(requestMatchers).isNotEmpty();
|
||||||
|
assertThat(requestMatchers.size()).isEqualTo(1);
|
||||||
|
assertThat(requestMatchers.get(0)).isExactlyInstanceOf(MvcRequestMatcher.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void requestMatchersWhenHttpMethodAndMvcPresentThenReturnMvcRequestMatcherType() throws Exception {
|
||||||
|
mockMvcPresentClasspath(true);
|
||||||
|
mockMvcIntrospector(true);
|
||||||
|
List<RequestMatcher> requestMatchers = this.matcherRegistry.requestMatchers(HttpMethod.GET);
|
||||||
|
assertThat(requestMatchers).isNotEmpty();
|
||||||
|
assertThat(requestMatchers.size()).isEqualTo(1);
|
||||||
|
assertThat(requestMatchers.get(0)).isExactlyInstanceOf(MvcRequestMatcher.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void requestMatchersWhenPatternAndMvcNotPresentThenReturnAntPathRequestMatcherType() throws Exception {
|
||||||
|
mockMvcPresentClasspath(false);
|
||||||
|
mockMvcIntrospector(false);
|
||||||
|
List<RequestMatcher> requestMatchers = this.matcherRegistry.requestMatchers("/path");
|
||||||
|
assertThat(requestMatchers).isNotEmpty();
|
||||||
|
assertThat(requestMatchers.size()).isEqualTo(1);
|
||||||
|
assertThat(requestMatchers.get(0)).isExactlyInstanceOf(AntPathRequestMatcher.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void requestMatchersWhenHttpMethodAndPatternAndMvcNotPresentThenReturnAntPathRequestMatcherType()
|
||||||
|
throws Exception {
|
||||||
|
mockMvcPresentClasspath(false);
|
||||||
|
mockMvcIntrospector(false);
|
||||||
|
List<RequestMatcher> requestMatchers = this.matcherRegistry.requestMatchers(HttpMethod.GET, "/path");
|
||||||
|
assertThat(requestMatchers).isNotEmpty();
|
||||||
|
assertThat(requestMatchers.size()).isEqualTo(1);
|
||||||
|
assertThat(requestMatchers.get(0)).isExactlyInstanceOf(AntPathRequestMatcher.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void requestMatchersWhenHttpMethodAndMvcNotPresentThenReturnAntPathMatcherType() throws Exception {
|
||||||
|
mockMvcPresentClasspath(false);
|
||||||
|
mockMvcIntrospector(false);
|
||||||
|
List<RequestMatcher> requestMatchers = this.matcherRegistry.requestMatchers(HttpMethod.GET);
|
||||||
|
assertThat(requestMatchers).isNotEmpty();
|
||||||
|
assertThat(requestMatchers.size()).isEqualTo(1);
|
||||||
|
assertThat(requestMatchers.get(0)).isExactlyInstanceOf(AntPathRequestMatcher.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void requestMatchersWhenMvcPresentInClassPathAndMvcIntrospectorBeanNotAvailableThenException()
|
||||||
|
throws Exception {
|
||||||
|
mockMvcPresentClasspath(true);
|
||||||
|
mockMvcIntrospector(false);
|
||||||
|
assertThatExceptionOfType(NoSuchBeanDefinitionException.class)
|
||||||
|
.isThrownBy(() -> this.matcherRegistry.requestMatchers("/path")).withMessageContaining(
|
||||||
|
"Please ensure Spring Security & Spring MVC are configured in a shared ApplicationContext");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void mockMvcIntrospector(boolean isPresent) {
|
||||||
|
ApplicationContext context = this.matcherRegistry.getApplicationContext();
|
||||||
|
given(context.containsBean("mvcHandlerMappingIntrospector")).willReturn(isPresent);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void mockMvcPresentClasspath(Object newValue) throws Exception {
|
||||||
|
Field mvcPresentField = AbstractRequestMatcherRegistry.class.getDeclaredField("mvcPresent");
|
||||||
|
mvcPresentField.setAccessible(true);
|
||||||
|
Field modifiersField = Field.class.getDeclaredField("modifiers");
|
||||||
|
modifiersField.setAccessible(true);
|
||||||
|
modifiersField.setInt(mvcPresentField, mvcPresentField.getModifiers() & ~Modifier.FINAL);
|
||||||
|
mvcPresentField.set(null, newValue);
|
||||||
|
}
|
||||||
|
|
||||||
private static class TestRequestMatcherRegistry extends AbstractRequestMatcherRegistry<List<RequestMatcher>> {
|
private static class TestRequestMatcherRegistry extends AbstractRequestMatcherRegistry<List<RequestMatcher>> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -20,6 +20,7 @@ import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.api.extension.ExtendWith;
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.http.HttpMethod;
|
import org.springframework.http.HttpMethod;
|
||||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||||
|
@ -27,11 +28,13 @@ import org.springframework.security.config.annotation.web.configuration.EnableWe
|
||||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||||
import org.springframework.security.config.test.SpringTestContext;
|
import org.springframework.security.config.test.SpringTestContext;
|
||||||
import org.springframework.security.config.test.SpringTestContextExtension;
|
import org.springframework.security.config.test.SpringTestContextExtension;
|
||||||
|
import org.springframework.security.web.SecurityFilterChain;
|
||||||
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
|
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
|
||||||
import org.springframework.security.web.util.matcher.RequestMatcher;
|
import org.springframework.security.web.util.matcher.RequestMatcher;
|
||||||
import org.springframework.test.web.servlet.MockMvc;
|
import org.springframework.test.web.servlet.MockMvc;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
|
||||||
|
|
||||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
|
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
|
||||||
|
@ -80,6 +83,22 @@ public class CsrfConfigurerIgnoringRequestMatchersTests {
|
||||||
this.mvc.perform(put("/no-csrf")).andExpect(status().isOk());
|
this.mvc.perform(put("/no-csrf")).andExpect(status().isOk());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void requestWhenIgnoringRequestMatcherPatternThenIgnores() throws Exception {
|
||||||
|
this.spring.register(IgnoringPathsAndMatchersPatternConfig.class, BasicController.class).autowire();
|
||||||
|
this.mvc.perform(put("/csrf")).andExpect(status().isForbidden());
|
||||||
|
this.mvc.perform(post("/csrf")).andExpect(status().isForbidden());
|
||||||
|
this.mvc.perform(put("/no-csrf")).andExpect(status().isOk());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void requestWhenIgnoringRequestMatcherPatternInLambdaThenIgnores() throws Exception {
|
||||||
|
this.spring.register(IgnoringPathsAndMatchersPatternInLambdaConfig.class, BasicController.class).autowire();
|
||||||
|
this.mvc.perform(put("/csrf")).andExpect(status().isForbidden());
|
||||||
|
this.mvc.perform(post("/csrf")).andExpect(status().isForbidden());
|
||||||
|
this.mvc.perform(put("/no-csrf")).andExpect(status().isOk());
|
||||||
|
}
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
@EnableWebSecurity
|
@EnableWebSecurity
|
||||||
static class IgnoringRequestMatchers extends WebSecurityConfigurerAdapter {
|
static class IgnoringRequestMatchers extends WebSecurityConfigurerAdapter {
|
||||||
|
@ -156,6 +175,41 @@ public class CsrfConfigurerIgnoringRequestMatchersTests {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@EnableWebSecurity
|
||||||
|
@EnableWebMvc
|
||||||
|
static class IgnoringPathsAndMatchersPatternConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
||||||
|
// @formatter:off
|
||||||
|
http
|
||||||
|
.csrf()
|
||||||
|
.ignoringRequestMatchers("/no-csrf");
|
||||||
|
// @formatter:on
|
||||||
|
return http.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@EnableWebSecurity
|
||||||
|
@EnableWebMvc
|
||||||
|
static class IgnoringPathsAndMatchersPatternInLambdaConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
||||||
|
// @formatter:off
|
||||||
|
http
|
||||||
|
.csrf((csrf) -> csrf
|
||||||
|
.ignoringRequestMatchers("/no-csrf")
|
||||||
|
);
|
||||||
|
// @formatter:on
|
||||||
|
return http.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
public static class BasicController {
|
public static class BasicController {
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,567 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2022 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.security.config.annotation.web.configurers;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.Modifier;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.AfterEach;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.context.annotation.Import;
|
||||||
|
import org.springframework.core.Ordered;
|
||||||
|
import org.springframework.core.annotation.Order;
|
||||||
|
import org.springframework.mock.web.MockFilterChain;
|
||||||
|
import org.springframework.mock.web.MockHttpServletRequest;
|
||||||
|
import org.springframework.mock.web.MockHttpServletResponse;
|
||||||
|
import org.springframework.mock.web.MockServletContext;
|
||||||
|
import org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry;
|
||||||
|
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||||
|
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||||
|
import org.springframework.security.core.userdetails.User;
|
||||||
|
import org.springframework.security.core.userdetails.UserDetails;
|
||||||
|
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||||
|
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
|
||||||
|
import org.springframework.security.web.FilterChainProxy;
|
||||||
|
import org.springframework.security.web.SecurityFilterChain;
|
||||||
|
import org.springframework.security.web.servlet.util.matcher.MvcRequestMatcher;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
|
||||||
|
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
|
||||||
|
import org.springframework.web.servlet.config.annotation.PathMatchConfigurer;
|
||||||
|
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||||
|
import org.springframework.web.servlet.handler.HandlerMappingIntrospector;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.springframework.security.config.Customizer.withDefaults;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Rob Winch
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class HttpSecuritySecurityMatchersTests {
|
||||||
|
|
||||||
|
AnnotationConfigWebApplicationContext context;
|
||||||
|
|
||||||
|
MockHttpServletRequest request;
|
||||||
|
|
||||||
|
MockHttpServletResponse response;
|
||||||
|
|
||||||
|
MockFilterChain chain;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
FilterChainProxy springSecurityFilterChain;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
public void setup() throws Exception {
|
||||||
|
this.request = new MockHttpServletRequest("GET", "");
|
||||||
|
this.request.setMethod("GET");
|
||||||
|
this.response = new MockHttpServletResponse();
|
||||||
|
this.chain = new MockFilterChain();
|
||||||
|
mockMvcPresentClasspath(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterEach
|
||||||
|
public void cleanup() {
|
||||||
|
if (this.context != null) {
|
||||||
|
this.context.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void securityMatcherWhenMvcThenMvcMatcher() throws Exception {
|
||||||
|
loadConfig(SecurityMatcherMvcConfig.class, LegacyMvcMatchingConfig.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);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void securityMatcherWhenNoMvcThenAntMatcher() throws Exception {
|
||||||
|
mockMvcPresentClasspath(false);
|
||||||
|
loadConfig(SecurityMatcherNoMvcConfig.class, LegacyMvcMatchingConfig.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_OK);
|
||||||
|
setup();
|
||||||
|
this.request.setServletPath("/path/");
|
||||||
|
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
|
||||||
|
assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_OK);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void securityMatcherWhenMvcMatcherAndGetFiltersNoUnsupportedMethodExceptionFromDummyRequest() {
|
||||||
|
loadConfig(SecurityMatcherMvcConfig.class);
|
||||||
|
assertThat(this.springSecurityFilterChain.getFilters("/path")).isNotEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void securityMatchersWhenMvcThenMvcMatcher() throws Exception {
|
||||||
|
loadConfig(SecurityMatchersMvcMatcherConfig.class, LegacyMvcMatchingConfig.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);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void securityMatchersWhenMvcMatcherInLambdaThenPathIsSecured() throws Exception {
|
||||||
|
loadConfig(SecurityMatchersMvcMatcherInLambdaConfig.class, LegacyMvcMatchingConfig.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);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void securityMatchersMvcMatcherServletPath() throws Exception {
|
||||||
|
loadConfig(SecurityMatchersMvcMatcherServletPathConfig.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);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void securityMatchersWhensMvcMatcherServletPathInLambdaThenPathIsSecured() throws Exception {
|
||||||
|
loadConfig(SecurityMatchersMvcMatcherServletPathInLambdaConfig.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);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void securityMatchersWhenMultiMvcMatcherInLambdaThenAllPathsAreDenied() throws Exception {
|
||||||
|
loadConfig(MultiMvcMatcherInLambdaConfig.class);
|
||||||
|
this.request.setRequestURI("/test-1");
|
||||||
|
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
|
||||||
|
assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_UNAUTHORIZED);
|
||||||
|
setup();
|
||||||
|
this.request.setRequestURI("/test-2");
|
||||||
|
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
|
||||||
|
assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_UNAUTHORIZED);
|
||||||
|
setup();
|
||||||
|
this.request.setRequestURI("/test-3");
|
||||||
|
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
|
||||||
|
assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_UNAUTHORIZED);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void securityMatchersWhenMultiMvcMatcherThenAllPathsAreDenied() throws Exception {
|
||||||
|
loadConfig(MultiMvcMatcherConfig.class);
|
||||||
|
this.request.setRequestURI("/test-1");
|
||||||
|
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
|
||||||
|
assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_UNAUTHORIZED);
|
||||||
|
setup();
|
||||||
|
this.request.setRequestURI("/test-2");
|
||||||
|
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
|
||||||
|
assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_UNAUTHORIZED);
|
||||||
|
setup();
|
||||||
|
this.request.setRequestURI("/test-3");
|
||||||
|
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
|
||||||
|
assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_UNAUTHORIZED);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void loadConfig(Class<?>... configs) {
|
||||||
|
this.context = new AnnotationConfigWebApplicationContext();
|
||||||
|
this.context.register(configs);
|
||||||
|
this.context.setServletContext(new MockServletContext());
|
||||||
|
this.context.refresh();
|
||||||
|
this.context.getAutowireCapableBeanFactory().autowireBean(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void mockMvcPresentClasspath(Object newValue) throws Exception {
|
||||||
|
mockMvcPresentClasspath(HttpSecurity.class, newValue);
|
||||||
|
mockMvcPresentClasspath(AbstractRequestMatcherRegistry.class, newValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void mockMvcPresentClasspath(Class<?> clazz, Object newValue) throws Exception {
|
||||||
|
Field mvcPresentField = clazz.getDeclaredField("mvcPresent");
|
||||||
|
mvcPresentField.setAccessible(true);
|
||||||
|
Field modifiersField = Field.class.getDeclaredField("modifiers");
|
||||||
|
modifiersField.setAccessible(true);
|
||||||
|
modifiersField.setInt(mvcPresentField, mvcPresentField.getModifiers() & ~Modifier.FINAL);
|
||||||
|
mvcPresentField.set(null, newValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
@EnableWebSecurity
|
||||||
|
@Configuration
|
||||||
|
@EnableWebMvc
|
||||||
|
static class MultiMvcMatcherInLambdaConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@Order(Ordered.HIGHEST_PRECEDENCE)
|
||||||
|
SecurityFilterChain first(HttpSecurity http) throws Exception {
|
||||||
|
// @formatter:off
|
||||||
|
http
|
||||||
|
.securityMatchers((requests) -> requests
|
||||||
|
.requestMatchers("/test-1")
|
||||||
|
.requestMatchers("/test-2")
|
||||||
|
.requestMatchers("/test-3")
|
||||||
|
)
|
||||||
|
.authorizeHttpRequests((authorize) -> authorize.anyRequest().denyAll())
|
||||||
|
.httpBasic(withDefaults());
|
||||||
|
// @formatter:on
|
||||||
|
return http.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
SecurityFilterChain second(HttpSecurity http) throws Exception {
|
||||||
|
// @formatter:off
|
||||||
|
http
|
||||||
|
.securityMatchers((requests) -> requests
|
||||||
|
.requestMatchers("/test-1")
|
||||||
|
)
|
||||||
|
.authorizeHttpRequests((authorize) -> authorize
|
||||||
|
.anyRequest().permitAll()
|
||||||
|
);
|
||||||
|
// @formatter:on
|
||||||
|
return http.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
static class PathController {
|
||||||
|
|
||||||
|
@RequestMapping({ "/test-1", "/test-2", "/test-3" })
|
||||||
|
String path() {
|
||||||
|
return "path";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@EnableWebSecurity
|
||||||
|
@Configuration
|
||||||
|
@EnableWebMvc
|
||||||
|
static class MultiMvcMatcherConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@Order(Ordered.HIGHEST_PRECEDENCE)
|
||||||
|
SecurityFilterChain first(HttpSecurity http) throws Exception {
|
||||||
|
// @formatter:off
|
||||||
|
http
|
||||||
|
.securityMatchers()
|
||||||
|
.requestMatchers("/test-1")
|
||||||
|
.requestMatchers("/test-2")
|
||||||
|
.requestMatchers("/test-3")
|
||||||
|
.and()
|
||||||
|
.authorizeHttpRequests()
|
||||||
|
.anyRequest().denyAll()
|
||||||
|
.and()
|
||||||
|
.httpBasic(withDefaults());
|
||||||
|
// @formatter:on
|
||||||
|
return http.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
SecurityFilterChain second(HttpSecurity http) throws Exception {
|
||||||
|
// @formatter:off
|
||||||
|
http
|
||||||
|
.securityMatchers()
|
||||||
|
.requestMatchers("/test-1")
|
||||||
|
.and()
|
||||||
|
.authorizeHttpRequests()
|
||||||
|
.anyRequest().permitAll();
|
||||||
|
// @formatter:on
|
||||||
|
return http.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
static class PathController {
|
||||||
|
|
||||||
|
@RequestMapping({ "/test-1", "/test-2", "/test-3" })
|
||||||
|
String path() {
|
||||||
|
return "path";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@EnableWebSecurity
|
||||||
|
@EnableWebMvc
|
||||||
|
@Configuration
|
||||||
|
@Import(UsersConfig.class)
|
||||||
|
static class SecurityMatcherMvcConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
SecurityFilterChain appSecurity(HttpSecurity http) throws Exception {
|
||||||
|
// @formatter:off
|
||||||
|
http
|
||||||
|
.securityMatcher("/path")
|
||||||
|
.httpBasic().and()
|
||||||
|
.authorizeHttpRequests()
|
||||||
|
.anyRequest().denyAll();
|
||||||
|
// @formatter:on
|
||||||
|
return http.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
static class PathController {
|
||||||
|
|
||||||
|
@RequestMapping("/path")
|
||||||
|
String path() {
|
||||||
|
return "path";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@EnableWebSecurity
|
||||||
|
@Configuration
|
||||||
|
@Import(UsersConfig.class)
|
||||||
|
static class SecurityMatcherNoMvcConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
SecurityFilterChain appSecurity(HttpSecurity http) throws Exception {
|
||||||
|
// @formatter:off
|
||||||
|
http
|
||||||
|
.securityMatcher("/path")
|
||||||
|
.httpBasic().and()
|
||||||
|
.authorizeHttpRequests()
|
||||||
|
.anyRequest().denyAll();
|
||||||
|
// @formatter:on
|
||||||
|
return http.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
static class PathController {
|
||||||
|
|
||||||
|
@RequestMapping("/path")
|
||||||
|
String path() {
|
||||||
|
return "path";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@EnableWebSecurity
|
||||||
|
@Configuration
|
||||||
|
@EnableWebMvc
|
||||||
|
@Import(UsersConfig.class)
|
||||||
|
static class SecurityMatchersMvcMatcherConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
SecurityFilterChain appSecurity(HttpSecurity http) throws Exception {
|
||||||
|
// @formatter:off
|
||||||
|
http
|
||||||
|
.securityMatchers()
|
||||||
|
.requestMatchers("/path")
|
||||||
|
.and()
|
||||||
|
.httpBasic().and()
|
||||||
|
.authorizeHttpRequests()
|
||||||
|
.anyRequest().denyAll();
|
||||||
|
// @formatter:on
|
||||||
|
return http.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
static class PathController {
|
||||||
|
|
||||||
|
@RequestMapping("/path")
|
||||||
|
String path() {
|
||||||
|
return "path";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@EnableWebSecurity
|
||||||
|
@Configuration
|
||||||
|
@EnableWebMvc
|
||||||
|
static class SecurityMatchersMvcMatcherInLambdaConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
SecurityFilterChain appSecurity(HttpSecurity http) throws Exception {
|
||||||
|
// @formatter:off
|
||||||
|
http
|
||||||
|
.securityMatchers((matchers) -> matchers
|
||||||
|
.requestMatchers("/path")
|
||||||
|
)
|
||||||
|
.httpBasic(withDefaults())
|
||||||
|
.authorizeHttpRequests((authorize) -> authorize
|
||||||
|
.anyRequest().denyAll()
|
||||||
|
);
|
||||||
|
// @formatter:on
|
||||||
|
return http.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
static class PathController {
|
||||||
|
|
||||||
|
@RequestMapping("/path")
|
||||||
|
String path() {
|
||||||
|
return "path";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@EnableWebSecurity
|
||||||
|
@Configuration
|
||||||
|
@EnableWebMvc
|
||||||
|
@Import(UsersConfig.class)
|
||||||
|
static class SecurityMatchersMvcMatcherServletPathConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
SecurityFilterChain appSecurity(HttpSecurity http, HandlerMappingIntrospector introspector) throws Exception {
|
||||||
|
MvcRequestMatcher.Builder mvcMatcherBuilder = new MvcRequestMatcher.Builder(introspector)
|
||||||
|
.servletPath("/spring");
|
||||||
|
// @formatter:off
|
||||||
|
http
|
||||||
|
.securityMatchers()
|
||||||
|
.requestMatchers(mvcMatcherBuilder.pattern("/path"))
|
||||||
|
.requestMatchers(mvcMatcherBuilder.pattern("/never-match"))
|
||||||
|
.and()
|
||||||
|
.httpBasic().and()
|
||||||
|
.authorizeHttpRequests()
|
||||||
|
.anyRequest().denyAll();
|
||||||
|
// @formatter:on
|
||||||
|
return http.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
static class PathController {
|
||||||
|
|
||||||
|
@RequestMapping("/path")
|
||||||
|
String path() {
|
||||||
|
return "path";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@EnableWebSecurity
|
||||||
|
@Configuration
|
||||||
|
@EnableWebMvc
|
||||||
|
@Import(UsersConfig.class)
|
||||||
|
static class SecurityMatchersMvcMatcherServletPathInLambdaConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
SecurityFilterChain appSecurity(HttpSecurity http, HandlerMappingIntrospector introspector) throws Exception {
|
||||||
|
MvcRequestMatcher.Builder mvcMatcherBuilder = new MvcRequestMatcher.Builder(introspector)
|
||||||
|
.servletPath("/spring");
|
||||||
|
// @formatter:off
|
||||||
|
http
|
||||||
|
.securityMatchers((matchers) -> matchers
|
||||||
|
.requestMatchers(mvcMatcherBuilder.pattern("/path"))
|
||||||
|
.requestMatchers(mvcMatcherBuilder.pattern("/never-match"))
|
||||||
|
)
|
||||||
|
.httpBasic(withDefaults())
|
||||||
|
.authorizeHttpRequests((authorize) -> authorize
|
||||||
|
.anyRequest().denyAll()
|
||||||
|
);
|
||||||
|
// @formatter:on
|
||||||
|
return http.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
static class PathController {
|
||||||
|
|
||||||
|
@RequestMapping("/path")
|
||||||
|
String path() {
|
||||||
|
return "path";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
static class UsersConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
UserDetailsService userDetailsService() {
|
||||||
|
UserDetails user = User.withDefaultPasswordEncoder().username("user").password("password").roles("USER")
|
||||||
|
.build();
|
||||||
|
return new InMemoryUserDetailsManager(user);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
static class LegacyMvcMatchingConfig implements WebMvcConfigurer {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void configurePathMatch(PathMatchConfigurer configurer) {
|
||||||
|
configurer.setUseSuffixPatternMatch(true);
|
||||||
|
configurer.setUseTrailingSlashMatch(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -44,6 +44,7 @@ import org.springframework.test.web.servlet.MockMvc
|
||||||
import org.springframework.test.web.servlet.post
|
import org.springframework.test.web.servlet.post
|
||||||
import org.springframework.web.bind.annotation.PostMapping
|
import org.springframework.web.bind.annotation.PostMapping
|
||||||
import org.springframework.web.bind.annotation.RestController
|
import org.springframework.web.bind.annotation.RestController
|
||||||
|
import org.springframework.web.servlet.config.annotation.EnableWebMvc
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests for [CsrfDsl]
|
* Tests for [CsrfDsl]
|
||||||
|
@ -279,6 +280,38 @@ class CsrfDslTests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `CSRF when ignoring request matchers pattern then CSRF disabled on matching requests`() {
|
||||||
|
this.spring.register(IgnoringRequestMatchersPatternConfig::class.java, BasicController::class.java).autowire()
|
||||||
|
|
||||||
|
this.mockMvc.post("/test1")
|
||||||
|
.andExpect {
|
||||||
|
status { isForbidden() }
|
||||||
|
}
|
||||||
|
|
||||||
|
this.mockMvc.post("/test2")
|
||||||
|
.andExpect {
|
||||||
|
status { isOk() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@EnableWebSecurity
|
||||||
|
@EnableWebMvc
|
||||||
|
open class IgnoringRequestMatchersPatternConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
open fun filterChain(http: HttpSecurity): SecurityFilterChain {
|
||||||
|
http {
|
||||||
|
csrf {
|
||||||
|
requireCsrfProtectionMatcher = AntPathRequestMatcher("/**")
|
||||||
|
ignoringRequestMatchers("/test2")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return http.build()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
internal class BasicController {
|
internal class BasicController {
|
||||||
@PostMapping("/test1")
|
@PostMapping("/test1")
|
||||||
|
|
|
@ -109,13 +109,14 @@ SecurityFilterChain web(HttpSecurity http, AuthorizationManager<RequestAuthoriza
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
AuthorizationManager<RequestAuthorizationContext> requestMatcherAuthorizationManager(HandlerMappingIntrospector introspector) {
|
AuthorizationManager<RequestAuthorizationContext> requestMatcherAuthorizationManager(HandlerMappingIntrospector introspector) {
|
||||||
|
MvcRequestMatcher.Builder mvcMatcherBuilder = new MvcRequestMatcher.Builder(introspector);
|
||||||
RequestMatcher permitAll =
|
RequestMatcher permitAll =
|
||||||
new AndRequestMatcher(
|
new AndRequestMatcher(
|
||||||
new MvcRequestMatcher(introspector, "/resources/**"),
|
mvcMatcherBuilder.pattern("/resources/**"),
|
||||||
new MvcRequestMatcher(introspector, "/signup"),
|
mvcMatcherBuilder.pattern("/signup"),
|
||||||
new MvcRequestMatcher(introspector, "/about"));
|
mvcMatcherBuilder.pattern("/about"));
|
||||||
RequestMatcher admin = new MvcRequestMatcher(introspector, "/admin/**");
|
RequestMatcher admin = mvcMatcherBuilder.pattern("/admin/**");
|
||||||
RequestMatcher db = new MvcRequestMatcher(introspector, "/db/**");
|
RequestMatcher db = mvcMatcherBuilder.pattern("/db/**");
|
||||||
RequestMatcher any = AnyRequestMatcher.INSTANCE;
|
RequestMatcher any = AnyRequestMatcher.INSTANCE;
|
||||||
AuthorizationManager<HttpRequestServlet> manager = RequestMatcherDelegatingAuthorizationManager.builder()
|
AuthorizationManager<HttpRequestServlet> manager = RequestMatcherDelegatingAuthorizationManager.builder()
|
||||||
.add(permitAll, (context) -> new AuthorizationDecision(true))
|
.add(permitAll, (context) -> new AuthorizationDecision(true))
|
||||||
|
|
|
@ -144,7 +144,7 @@ You could then refer to the method as follows:
|
||||||
----
|
----
|
||||||
http
|
http
|
||||||
.authorizeHttpRequests(authorize -> authorize
|
.authorizeHttpRequests(authorize -> authorize
|
||||||
.antMatchers("/user/**").access("@webSecurity.check(authentication,request)")
|
.requestMatchers("/user/**").access("@webSecurity.check(authentication,request)")
|
||||||
...
|
...
|
||||||
)
|
)
|
||||||
----
|
----
|
||||||
|
@ -210,7 +210,7 @@ You could then refer to the method as follows:
|
||||||
----
|
----
|
||||||
http
|
http
|
||||||
.authorizeHttpRequests(authorize -> authorize
|
.authorizeHttpRequests(authorize -> authorize
|
||||||
.antMatchers("/user/{userId}/**").access("@webSecurity.checkUserId(authentication,#userId)")
|
.requestMatchers("/user/{userId}/**").access("@webSecurity.checkUserId(authentication,#userId)")
|
||||||
...
|
...
|
||||||
);
|
);
|
||||||
----
|
----
|
||||||
|
|
|
@ -151,8 +151,8 @@ To restrict access to this controller method to admin users, you can provide aut
|
||||||
@Bean
|
@Bean
|
||||||
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
||||||
http
|
http
|
||||||
.authorizeHttpRequests(authorize -> authorize
|
.authorizeHttpRequests((authorize) -> authorize
|
||||||
.antMatchers("/admin").hasRole("ADMIN")
|
.requestMatchers("/admin").hasRole("ADMIN")
|
||||||
);
|
);
|
||||||
return http.build();
|
return http.build();
|
||||||
}
|
}
|
||||||
|
@ -164,8 +164,8 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
||||||
@Bean
|
@Bean
|
||||||
open fun filterChain(http: HttpSecurity): SecurityFilterChain {
|
open fun filterChain(http: HttpSecurity): SecurityFilterChain {
|
||||||
http {
|
http {
|
||||||
authorizeRequests {
|
authorizeHttpRequests {
|
||||||
authorize(AntPathRequestMatcher("/admin"), hasRole("ADMIN"))
|
authorize("/admin", hasRole("ADMIN"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return http.build()
|
return http.build()
|
||||||
|
@ -191,20 +191,24 @@ Additionally, depending on our Spring MVC configuration, the `/admin` URL also m
|
||||||
The problem is that our security rule protects only `/admin`.
|
The problem is that our security rule protects only `/admin`.
|
||||||
We could add additional rules for all the permutations of Spring MVC, but this would be quite verbose and tedious.
|
We could add additional rules for all the permutations of Spring MVC, but this would be quite verbose and tedious.
|
||||||
|
|
||||||
Instead, we can use Spring Security's `MvcRequestMatcher`.
|
Fortunately, when using the `requestMatchers` DSL method, Spring Security automatically creates a `MvcRequestMatcher` if it detects that Spring MVC is available in the classpath.
|
||||||
The following configuration protects the same URLs that Spring MVC matches on by using Spring MVC to match on the URL.
|
Therefore, it will protect the same URLs that Spring MVC will match on by using Spring MVC to match on the URL.
|
||||||
|
|
||||||
|
One common requirement when using Spring MVC is to specify the servlet path property, for that you can use the `MvcRequestMatcher.Builder` to create multiple `MvcRequestMatcher` instances that share the same servlet path:
|
||||||
|
|
||||||
====
|
====
|
||||||
.Java
|
.Java
|
||||||
[source,java,role="primary"]
|
[source,java,role="primary"]
|
||||||
----
|
----
|
||||||
@Bean
|
@Bean
|
||||||
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
public SecurityFilterChain filterChain(HttpSecurity http, HandlerMappingIntrospector introspector) throws Exception {
|
||||||
|
MvcRequestMatcher.Builder mvcMatcherBuilder = new MvcRequestMatcher.Builder(introspector).servletPath("/path");
|
||||||
http
|
http
|
||||||
.authorizeHttpRequests(authorize -> authorize
|
.authorizeHttpRequests((authorize) -> authorize
|
||||||
.mvcMatchers("/admin").hasRole("ADMIN")
|
.requestMatchers(mvcMatcherBuilder.pattern("/admin")).hasRole("ADMIN")
|
||||||
|
.requestMatchers(mvcMatcherBuilder.pattern("/user")).hasRole("USER")
|
||||||
);
|
);
|
||||||
// ...
|
return http.build();
|
||||||
}
|
}
|
||||||
----
|
----
|
||||||
|
|
||||||
|
@ -212,13 +216,15 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
||||||
[source,kotlin,role="secondary"]
|
[source,kotlin,role="secondary"]
|
||||||
----
|
----
|
||||||
@Bean
|
@Bean
|
||||||
open fun filterChain(http: HttpSecurity): SecurityFilterChain {
|
open fun filterChain(http: HttpSecurity, introspector: HandlerMappingIntrospector): SecurityFilterChain {
|
||||||
|
val mvcMatcherBuilder = MvcRequestMatcher.Builder(introspector)
|
||||||
http {
|
http {
|
||||||
authorizeRequests {
|
authorizeHttpRequests {
|
||||||
authorize("/admin", hasRole("ADMIN"))
|
authorize(mvcMatcherBuilder.pattern("/admin"), hasRole("ADMIN"))
|
||||||
|
authorize(mvcMatcherBuilder.pattern("/user"), hasRole("USER"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// ...
|
return http.build()
|
||||||
}
|
}
|
||||||
----
|
----
|
||||||
====
|
====
|
||||||
|
|
|
@ -584,7 +584,7 @@ public class WebSecurityConfig {
|
||||||
http
|
http
|
||||||
.csrf(csrf -> csrf
|
.csrf(csrf -> csrf
|
||||||
// ignore our stomp endpoints since they are protected using Stomp headers
|
// ignore our stomp endpoints since they are protected using Stomp headers
|
||||||
.ignoringAntMatchers("/chat/**")
|
.ignoringRequestMatchers("/chat/**")
|
||||||
)
|
)
|
||||||
.headers(headers -> headers
|
.headers(headers -> headers
|
||||||
// allow same origin to frame our site to support iframe SockJS
|
// allow same origin to frame our site to support iframe SockJS
|
||||||
|
@ -610,7 +610,7 @@ open class WebSecurityConfig {
|
||||||
open fun filterChain(http: HttpSecurity): SecurityFilterChain {
|
open fun filterChain(http: HttpSecurity): SecurityFilterChain {
|
||||||
http {
|
http {
|
||||||
csrf {
|
csrf {
|
||||||
ignoringAntMatchers("/chat/**")
|
ignoringRequestMatchers("/chat/**")
|
||||||
}
|
}
|
||||||
headers {
|
headers {
|
||||||
frameOptions {
|
frameOptions {
|
||||||
|
|
|
@ -17,4 +17,4 @@ Reorganize imports
|
||||||
Reorganize imports
|
Reorganize imports
|
||||||
* https://github.com/spring-projects/spring-security/issues/11026[gh-11026] - Use `RequestAttributeSecurityContextRepository` instead of `NullSecurityContextRepository`
|
* https://github.com/spring-projects/spring-security/issues/11026[gh-11026] - Use `RequestAttributeSecurityContextRepository` instead of `NullSecurityContextRepository`
|
||||||
* https://github.com/spring-projects/spring-security/pull/11887[gh-11827] - Change default authority for `oauth2Login()`
|
* https://github.com/spring-projects/spring-security/pull/11887[gh-11827] - Change default authority for `oauth2Login()`
|
||||||
* https://github.com/spring-projects/spring-security/issues/10347[gh-10347] - Remove `UsernamePasswordAuthenticationToken` check in `BasicAuthenticationFilter`
|
* https://github.com/spring-projects/spring-security/issues/10347[gh-10347] - Remove `UsernamePasswordAuthenticationToken` check in `BasicAuthenticationFilter`
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2012-2021 the original author or authors.
|
* Copyright 2012-2022 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.
|
||||||
|
@ -172,4 +172,59 @@ public class MvcRequestMatcher implements RequestMatcher, RequestVariablesExtrac
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A builder for {@link MvcRequestMatcher}
|
||||||
|
*
|
||||||
|
* @author Marcus Da Coregio
|
||||||
|
* @since 5.8
|
||||||
|
*/
|
||||||
|
public static final class Builder {
|
||||||
|
|
||||||
|
private final HandlerMappingIntrospector introspector;
|
||||||
|
|
||||||
|
private String servletPath;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a new instance of this builder
|
||||||
|
*/
|
||||||
|
public Builder(HandlerMappingIntrospector introspector) {
|
||||||
|
this.introspector = introspector;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the servlet path to be used by the {@link MvcRequestMatcher} generated by
|
||||||
|
* this builder
|
||||||
|
* @param servletPath the servlet path to use
|
||||||
|
* @return the {@link Builder} for further configuration
|
||||||
|
*/
|
||||||
|
public Builder servletPath(String servletPath) {
|
||||||
|
this.servletPath = servletPath;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an {@link MvcRequestMatcher} that uses the provided pattern to match
|
||||||
|
* @param pattern the pattern used to match
|
||||||
|
* @return the generated {@link MvcRequestMatcher}
|
||||||
|
*/
|
||||||
|
public MvcRequestMatcher pattern(String pattern) {
|
||||||
|
return pattern(null, pattern);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an {@link MvcRequestMatcher} that uses the provided pattern and HTTP
|
||||||
|
* method to match
|
||||||
|
* @param method the {@link HttpMethod}, can be null
|
||||||
|
* @param pattern the patterns used to match
|
||||||
|
* @return the generated {@link MvcRequestMatcher}
|
||||||
|
*/
|
||||||
|
public MvcRequestMatcher pattern(HttpMethod method, String pattern) {
|
||||||
|
MvcRequestMatcher mvcRequestMatcher = new MvcRequestMatcher(this.introspector, pattern);
|
||||||
|
mvcRequestMatcher.setServletPath(this.servletPath);
|
||||||
|
mvcRequestMatcher.setMethod(method);
|
||||||
|
return mvcRequestMatcher;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2012-2021 the original author or authors.
|
* Copyright 2012-2022 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.
|
||||||
|
@ -28,6 +28,7 @@ import org.mockito.junit.jupiter.MockitoExtension;
|
||||||
|
|
||||||
import org.springframework.http.HttpMethod;
|
import org.springframework.http.HttpMethod;
|
||||||
import org.springframework.mock.web.MockHttpServletRequest;
|
import org.springframework.mock.web.MockHttpServletRequest;
|
||||||
|
import org.springframework.test.util.ReflectionTestUtils;
|
||||||
import org.springframework.web.HttpRequestMethodNotSupportedException;
|
import org.springframework.web.HttpRequestMethodNotSupportedException;
|
||||||
import org.springframework.web.servlet.handler.HandlerMappingIntrospector;
|
import org.springframework.web.servlet.handler.HandlerMappingIntrospector;
|
||||||
import org.springframework.web.servlet.handler.MatchableHandlerMapping;
|
import org.springframework.web.servlet.handler.MatchableHandlerMapping;
|
||||||
|
@ -245,4 +246,30 @@ public class MvcRequestMatcherTests {
|
||||||
assertThat(this.matcher.matcher(this.request).isMatch()).isTrue();
|
assertThat(this.matcher.matcher(this.request).isMatch()).isTrue();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void builderWhenServletPathThenServletPathPresent() {
|
||||||
|
MvcRequestMatcher matcher = new MvcRequestMatcher.Builder(this.introspector).servletPath("/path")
|
||||||
|
.pattern("/endpoint");
|
||||||
|
assertThat(matcher.getServletPath()).isEqualTo("/path");
|
||||||
|
assertThat(ReflectionTestUtils.getField(matcher, "pattern")).isEqualTo("/endpoint");
|
||||||
|
assertThat(ReflectionTestUtils.getField(matcher, "method")).isNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void builderWhenPatternThenPatternPresent() {
|
||||||
|
MvcRequestMatcher matcher = new MvcRequestMatcher.Builder(this.introspector).pattern("/endpoint");
|
||||||
|
assertThat(matcher.getServletPath()).isNull();
|
||||||
|
assertThat(ReflectionTestUtils.getField(matcher, "pattern")).isEqualTo("/endpoint");
|
||||||
|
assertThat(ReflectionTestUtils.getField(matcher, "method")).isNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void builderWhenMethodAndPatternThenMethodAndPatternPresent() {
|
||||||
|
MvcRequestMatcher matcher = new MvcRequestMatcher.Builder(this.introspector).pattern(HttpMethod.GET,
|
||||||
|
"/endpoint");
|
||||||
|
assertThat(matcher.getServletPath()).isNull();
|
||||||
|
assertThat(ReflectionTestUtils.getField(matcher, "pattern")).isEqualTo("/endpoint");
|
||||||
|
assertThat(ReflectionTestUtils.getField(matcher, "method")).isEqualTo(HttpMethod.GET);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue