Remove authorizeRequests

Closes gh-15174
This commit is contained in:
Josh Cummings 2025-07-09 16:24:48 -06:00
parent 2c87270dbc
commit f1725b25a0
No known key found for this signature in database
GPG Key ID: 869B37A20E876129
14 changed files with 4 additions and 3886 deletions

View File

@ -53,7 +53,6 @@ import org.springframework.security.config.annotation.web.configurers.ChannelSec
import org.springframework.security.config.annotation.web.configurers.CorsConfigurer;
import org.springframework.security.config.annotation.web.configurers.CsrfConfigurer;
import org.springframework.security.config.annotation.web.configurers.ExceptionHandlingConfigurer;
import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer;
import org.springframework.security.config.annotation.web.configurers.FormLoginConfigurer;
import org.springframework.security.config.annotation.web.configurers.HeadersConfigurer;
import org.springframework.security.config.annotation.web.configurers.HttpBasicConfigurer;
@ -613,125 +612,6 @@ public final class HttpSecurity extends AbstractConfiguredSecurityBuilder<Defaul
return HttpSecurity.this;
}
/**
* Allows restricting access based upon the {@link HttpServletRequest} using
* {@link RequestMatcher} implementations (i.e. via URL patterns).
*
* <h2>Example Configurations</h2>
*
* The most basic example is to configure all URLs to require the role "ROLE_USER".
* The configuration below requires authentication to every URL and will grant access
* to both the user "admin" and "user".
*
* <pre>
* &#064;Configuration
* &#064;EnableWebSecurity
* public class AuthorizeUrlsSecurityConfig {
*
* &#064;Bean
* public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
* http
* .authorizeRequests((authorizeRequests) -&gt;
* authorizeRequests
* .requestMatchers(&quot;/**&quot;).hasRole(&quot;USER&quot;)
* )
* .formLogin(withDefaults());
* return http.build();
* }
*
* &#064;Bean
* public UserDetailsService userDetailsService() {
* UserDetails user = User.withDefaultPasswordEncoder()
* .username(&quot;user&quot;)
* .password(&quot;password&quot;)
* .roles(&quot;USER&quot;)
* .build();
* UserDetails admin = User.withDefaultPasswordEncoder()
* .username(&quot;admin&quot;)
* .password(&quot;password&quot;)
* .roles(&quot;ADMIN&quot;, &quot;USER&quot;)
* .build();
* return new InMemoryUserDetailsManager(user, admin);
* }
* }
* </pre>
*
* We can also configure multiple URLs. The configuration below requires
* authentication to every URL and will grant access to URLs starting with /admin/ to
* only the "admin" user. All other URLs either user can access.
*
* <pre>
* &#064;Configuration
* &#064;EnableWebSecurity
* public class AuthorizeUrlsSecurityConfig {
*
* &#064;Bean
* public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
* http
* .authorizeRequests((authorizeRequests) -&gt;
* authorizeRequests
* .requestMatchers(&quot;/admin/**&quot;).hasRole(&quot;ADMIN&quot;)
* .requestMatchers(&quot;/**&quot;).hasRole(&quot;USER&quot;)
* )
* .formLogin(withDefaults());
* return http.build();
* }
*
* &#064;Bean
* public UserDetailsService userDetailsService() {
* UserDetails user = User.withDefaultPasswordEncoder()
* .username(&quot;user&quot;)
* .password(&quot;password&quot;)
* .roles(&quot;USER&quot;)
* .build();
* UserDetails admin = User.withDefaultPasswordEncoder()
* .username(&quot;admin&quot;)
* .password(&quot;password&quot;)
* .roles(&quot;ADMIN&quot;, &quot;USER&quot;)
* .build();
* return new InMemoryUserDetailsManager(user, admin);
* }
* }
* </pre>
*
* Note that the matchers are considered in order. Therefore, the following is invalid
* because the first matcher matches every request and will never get to the second
* mapping:
*
* <pre>
* &#064;Configuration
* &#064;EnableWebSecurity
* public class AuthorizeUrlsSecurityConfig {
*
* &#064;Bean
* public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
* http
* .authorizeRequests((authorizeRequests) -&gt;
* authorizeRequests
* .requestMatchers(&quot;/**&quot;).hasRole(&quot;USER&quot;)
* .requestMatchers(&quot;/admin/**&quot;).hasRole(&quot;ADMIN&quot;)
* );
* return http.build();
* }
* }
* </pre>
* @param authorizeRequestsCustomizer the {@link Customizer} to provide more options
* for the {@link ExpressionUrlAuthorizationConfigurer.ExpressionInterceptUrlRegistry}
* @return the {@link HttpSecurity} for further customizations
* @throws Exception
* @deprecated For removal in 7.0. Use {@link #authorizeHttpRequests(Customizer)}
* instead
*/
@Deprecated(since = "6.1", forRemoval = true)
public HttpSecurity authorizeRequests(
Customizer<ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry> authorizeRequestsCustomizer)
throws Exception {
ApplicationContext context = getContext();
authorizeRequestsCustomizer
.customize(getOrApply(new ExpressionUrlAuthorizationConfigurer<>(context)).getRegistry());
return HttpSecurity.this;
}
/**
* Allows restricting access based upon the {@link HttpServletRequest} using
* {@link RequestMatcher} implementations (i.e. via URL patterns).
@ -1936,12 +1816,6 @@ public final class HttpSecurity extends AbstractConfiguredSecurityBuilder<Defaul
@SuppressWarnings("unchecked")
@Override
protected DefaultSecurityFilterChain performBuild() {
ExpressionUrlAuthorizationConfigurer<?> expressionConfigurer = getConfigurer(
ExpressionUrlAuthorizationConfigurer.class);
AuthorizeHttpRequestsConfigurer<?> httpConfigurer = getConfigurer(AuthorizeHttpRequestsConfigurer.class);
boolean oneConfigurerPresent = expressionConfigurer == null ^ httpConfigurer == null;
Assert.state((expressionConfigurer == null && httpConfigurer == null) || oneConfigurerPresent,
"authorizeHttpRequests cannot be used in conjunction with authorizeRequests. Please select just one.");
this.filters.sort(OrderComparator.INSTANCE);
List<Filter> sortedFilters = new ArrayList<>(this.filters.size());
for (Filter filter : this.filters) {

View File

@ -36,8 +36,6 @@ import org.springframework.util.Assert;
* @author Rob Winch
* @since 3.2
* @see ChannelSecurityConfigurer
* @see UrlAuthorizationConfigurer
* @see ExpressionUrlAuthorizationConfigurer
* @deprecated In modern Spring Security APIs, each API manages its own configuration
* context. As such there is no direct replacement for this interface. In the case of
* method security, please see {@link SecurityAnnotationScanner} and

View File

@ -1,196 +0,0 @@
/*
* Copyright 2002-2025 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.util.List;
import org.springframework.security.access.AccessDecisionManager;
import org.springframework.security.access.AccessDecisionVoter;
import org.springframework.security.access.vote.AffirmativeBased;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.SecurityConfigurer;
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
/**
* A base class for configuring the {@link FilterSecurityInterceptor}.
*
* <h2>Security Filters</h2>
*
* The following Filters are populated
*
* <ul>
* <li>{@link FilterSecurityInterceptor}</li>
* </ul>
*
* <h2>Shared Objects Created</h2>
*
* The following shared objects are populated to allow other {@link SecurityConfigurer}'s
* to customize:
* <ul>
* <li>{@link FilterSecurityInterceptor}</li>
* </ul>
*
* <h2>Shared Objects Used</h2>
*
* The following shared objects are used:
*
* <ul>
* <li>{@link AuthenticationManager}</li>
* </ul>
*
* @param <C> the AbstractInterceptUrlConfigurer
* @param <H> the type of {@link HttpSecurityBuilder} that is being configured
* @author Rob Winch
* @since 3.2
* @see ExpressionUrlAuthorizationConfigurer
* @see UrlAuthorizationConfigurer
* @deprecated Use {@link AuthorizeHttpRequestsConfigurer} instead
*/
@Deprecated
public abstract class AbstractInterceptUrlConfigurer<C extends AbstractInterceptUrlConfigurer<C, H>, H extends HttpSecurityBuilder<H>>
extends AbstractHttpConfigurer<C, H> {
private Boolean filterSecurityInterceptorOncePerRequest;
private AccessDecisionManager accessDecisionManager;
AbstractInterceptUrlConfigurer() {
}
@Override
public void configure(H http) throws Exception {
FilterInvocationSecurityMetadataSource metadataSource = createMetadataSource(http);
if (metadataSource == null) {
return;
}
FilterSecurityInterceptor securityInterceptor = createFilterSecurityInterceptor(http, metadataSource,
http.getSharedObject(AuthenticationManager.class));
if (this.filterSecurityInterceptorOncePerRequest != null) {
securityInterceptor.setObserveOncePerRequest(this.filterSecurityInterceptorOncePerRequest);
}
securityInterceptor = postProcess(securityInterceptor);
http.addFilter(securityInterceptor);
http.setSharedObject(FilterSecurityInterceptor.class, securityInterceptor);
}
/**
* Subclasses should implement this method to provide a
* {@link FilterInvocationSecurityMetadataSource} for the
* {@link FilterSecurityInterceptor}.
* @param http the builder to use
* @return the {@link FilterInvocationSecurityMetadataSource} to set on the
* {@link FilterSecurityInterceptor}. Cannot be null.
*/
abstract FilterInvocationSecurityMetadataSource createMetadataSource(H http);
/**
* Subclasses should implement this method to provide the {@link AccessDecisionVoter}
* instances used to create the default {@link AccessDecisionManager}
* @param http the builder to use
* @return the {@link AccessDecisionVoter} instances used to create the default
* {@link AccessDecisionManager}
*/
abstract List<AccessDecisionVoter<?>> getDecisionVoters(H http);
/**
* Creates the default {@code AccessDecisionManager}
* @return the default {@code AccessDecisionManager}
*/
private AccessDecisionManager createDefaultAccessDecisionManager(H http) {
AffirmativeBased result = new AffirmativeBased(getDecisionVoters(http));
return postProcess(result);
}
/**
* If currently null, creates a default {@link AccessDecisionManager} using
* {@link #createDefaultAccessDecisionManager(HttpSecurityBuilder)}. Otherwise returns
* the {@link AccessDecisionManager}.
* @param http the builder to use
* @return the {@link AccessDecisionManager} to use
*/
private AccessDecisionManager getAccessDecisionManager(H http) {
if (this.accessDecisionManager == null) {
this.accessDecisionManager = createDefaultAccessDecisionManager(http);
}
return this.accessDecisionManager;
}
/**
* Creates the {@link FilterSecurityInterceptor}
* @param http the builder to use
* @param metadataSource the {@link FilterInvocationSecurityMetadataSource} to use
* @param authenticationManager the {@link AuthenticationManager} to use
* @return the {@link FilterSecurityInterceptor}
* @throws Exception
*/
private FilterSecurityInterceptor createFilterSecurityInterceptor(H http,
FilterInvocationSecurityMetadataSource metadataSource, AuthenticationManager authenticationManager)
throws Exception {
FilterSecurityInterceptor securityInterceptor = new FilterSecurityInterceptor();
securityInterceptor.setSecurityMetadataSource(metadataSource);
securityInterceptor.setAccessDecisionManager(getAccessDecisionManager(http));
securityInterceptor.setAuthenticationManager(authenticationManager);
securityInterceptor.setSecurityContextHolderStrategy(getSecurityContextHolderStrategy());
securityInterceptor.afterPropertiesSet();
return securityInterceptor;
}
@Deprecated
public abstract class AbstractInterceptUrlRegistry<R extends AbstractInterceptUrlRegistry<R, T>, T>
extends AbstractConfigAttributeRequestMatcherRegistry<T> {
AbstractInterceptUrlRegistry() {
}
/**
* Allows setting the {@link AccessDecisionManager}. If none is provided, a
* default {@link AccessDecisionManager} is created.
* @param accessDecisionManager the {@link AccessDecisionManager} to use
* @return the {@link AbstractInterceptUrlConfigurer} for further customization
*/
public R accessDecisionManager(AccessDecisionManager accessDecisionManager) {
AbstractInterceptUrlConfigurer.this.accessDecisionManager = accessDecisionManager;
return getSelf();
}
/**
* Allows setting if the {@link FilterSecurityInterceptor} should be only applied
* once per request (i.e. if the filter intercepts on a forward, should it be
* applied again).
* @param filterSecurityInterceptorOncePerRequest if the
* {@link FilterSecurityInterceptor} should be only applied once per request
* @return the {@link AbstractInterceptUrlConfigurer} for further customization
*/
public R filterSecurityInterceptorOncePerRequest(boolean filterSecurityInterceptorOncePerRequest) {
AbstractInterceptUrlConfigurer.this.filterSecurityInterceptorOncePerRequest = filterSecurityInterceptorOncePerRequest;
return getSelf();
}
/**
* Returns a reference to the current object with a single suppression of the type
* @return a reference to the current object
*/
@SuppressWarnings("unchecked")
private R getSelf() {
return (R) this;
}
}
}

View File

@ -1,412 +0,0 @@
/*
* Copyright 2002-2025 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.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import org.springframework.context.ApplicationContext;
import org.springframework.security.access.AccessDecisionVoter;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.PermissionEvaluator;
import org.springframework.security.access.SecurityConfig;
import org.springframework.security.access.expression.SecurityExpressionHandler;
import org.springframework.security.access.hierarchicalroles.RoleHierarchy;
import org.springframework.security.authentication.AuthenticationTrustResolver;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.ObjectPostProcessor;
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.core.GrantedAuthorityDefaults;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler;
import org.springframework.security.web.access.expression.ExpressionBasedFilterInvocationSecurityMetadataSource;
import org.springframework.security.web.access.expression.WebExpressionVoter;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
* Adds URL based authorization based upon SpEL expressions to an application. At least
* one {@link org.springframework.web.bind.annotation.RequestMapping} needs to be mapped
* to {@link ConfigAttribute}'s for this {@link SecurityContextConfigurer} to have
* meaning.
* <h2>Security Filters</h2>
*
* The following Filters are populated
*
* <ul>
* <li>{@link org.springframework.security.web.access.intercept.FilterSecurityInterceptor}
* </li>
* </ul>
*
* <h2>Shared Objects Created</h2>
*
* The following shared objects are populated to allow other
* {@link org.springframework.security.config.annotation.SecurityConfigurer}'s to
* customize:
* <ul>
* <li>{@link org.springframework.security.web.access.intercept.FilterSecurityInterceptor}
* </li>
* </ul>
*
* <h2>Shared Objects Used</h2>
*
* <ul>
* <li>{@link AuthenticationTrustResolver} is optionally used to populate the
* {@link DefaultWebSecurityExpressionHandler}</li>
* </ul>
*
* @param <H> the type of {@link HttpSecurityBuilder} that is being configured
* @author Rob Winch
* @author Yanming Zhou
* @author Ngoc Nhan
* @since 3.2
* @see org.springframework.security.config.annotation.web.builders.HttpSecurity#authorizeRequests(Customizer)
* @deprecated Use {@link AuthorizeHttpRequestsConfigurer} instead
*/
@Deprecated
public final class ExpressionUrlAuthorizationConfigurer<H extends HttpSecurityBuilder<H>>
extends AbstractInterceptUrlConfigurer<ExpressionUrlAuthorizationConfigurer<H>, H> {
static final String permitAll = "permitAll";
private static final String denyAll = "denyAll";
private static final String anonymous = "anonymous";
private static final String authenticated = "authenticated";
private static final String fullyAuthenticated = "fullyAuthenticated";
private static final String rememberMe = "rememberMe";
private final String rolePrefix;
private final ExpressionInterceptUrlRegistry REGISTRY;
private SecurityExpressionHandler<FilterInvocation> expressionHandler;
/**
* Creates a new instance
* @see HttpSecurity#authorizeRequests(Customizer)
*/
public ExpressionUrlAuthorizationConfigurer(ApplicationContext context) {
GrantedAuthorityDefaults grantedAuthorityDefaults = context.getBeanProvider(GrantedAuthorityDefaults.class)
.getIfUnique();
if (grantedAuthorityDefaults != null) {
this.rolePrefix = grantedAuthorityDefaults.getRolePrefix();
}
else {
this.rolePrefix = "ROLE_";
}
this.REGISTRY = new ExpressionInterceptUrlRegistry(context);
}
public ExpressionInterceptUrlRegistry getRegistry() {
return this.REGISTRY;
}
/**
* Allows registering multiple {@link RequestMatcher} instances to a collection of
* {@link ConfigAttribute} instances
* @param requestMatchers the {@link RequestMatcher} instances to register to the
* {@link ConfigAttribute} instances
* @param configAttributes the {@link ConfigAttribute} to be mapped by the
* {@link RequestMatcher} instances
*/
private void interceptUrl(Iterable<? extends RequestMatcher> requestMatchers,
Collection<ConfigAttribute> configAttributes) {
for (RequestMatcher requestMatcher : requestMatchers) {
this.REGISTRY.addMapping(
new AbstractConfigAttributeRequestMatcherRegistry.UrlMapping(requestMatcher, configAttributes));
}
}
@Override
@SuppressWarnings("rawtypes")
List<AccessDecisionVoter<?>> getDecisionVoters(H http) {
List<AccessDecisionVoter<?>> decisionVoters = new ArrayList<>();
WebExpressionVoter expressionVoter = new WebExpressionVoter();
expressionVoter.setExpressionHandler(getExpressionHandler(http));
decisionVoters.add(expressionVoter);
return decisionVoters;
}
@Override
ExpressionBasedFilterInvocationSecurityMetadataSource createMetadataSource(H http) {
LinkedHashMap<RequestMatcher, Collection<ConfigAttribute>> requestMap = this.REGISTRY.createRequestMap();
Assert.state(!requestMap.isEmpty(),
"At least one mapping is required (i.e. authorizeRequests().anyRequest().authenticated())");
return new ExpressionBasedFilterInvocationSecurityMetadataSource(requestMap, getExpressionHandler(http));
}
private SecurityExpressionHandler<FilterInvocation> getExpressionHandler(H http) {
if (this.expressionHandler != null) {
return this.expressionHandler;
}
DefaultWebSecurityExpressionHandler defaultHandler = new DefaultWebSecurityExpressionHandler();
AuthenticationTrustResolver trustResolver = http.getSharedObject(AuthenticationTrustResolver.class);
if (trustResolver != null) {
defaultHandler.setTrustResolver(trustResolver);
}
ApplicationContext context = http.getSharedObject(ApplicationContext.class);
if (context != null) {
context.getBeanProvider(RoleHierarchy.class).ifUnique(defaultHandler::setRoleHierarchy);
context.getBeanProvider(GrantedAuthorityDefaults.class)
.ifUnique((grantedAuthorityDefaults) -> defaultHandler
.setDefaultRolePrefix(grantedAuthorityDefaults.getRolePrefix()));
context.getBeanProvider(PermissionEvaluator.class).ifUnique(defaultHandler::setPermissionEvaluator);
}
this.expressionHandler = postProcess(defaultHandler);
return this.expressionHandler;
}
private static String hasAnyRole(String rolePrefix, String... authorities) {
String anyAuthorities = StringUtils.arrayToDelimitedString(authorities, "','" + rolePrefix);
return "hasAnyRole('" + rolePrefix + anyAuthorities + "')";
}
private static String hasRole(String rolePrefix, String role) {
Assert.notNull(role, "role cannot be null");
Assert.isTrue(rolePrefix.isEmpty() || !role.startsWith(rolePrefix), () -> "role should not start with '"
+ rolePrefix + "' since it is automatically inserted. Got '" + role + "'");
return "hasRole('" + rolePrefix + role + "')";
}
private static String hasAuthority(String authority) {
return "hasAuthority('" + authority + "')";
}
private static String hasAnyAuthority(String... authorities) {
String anyAuthorities = StringUtils.arrayToDelimitedString(authorities, "','");
return "hasAnyAuthority('" + anyAuthorities + "')";
}
private static String hasIpAddress(String ipAddressExpression) {
return "hasIpAddress('" + ipAddressExpression + "')";
}
@Deprecated
public final class ExpressionInterceptUrlRegistry extends
ExpressionUrlAuthorizationConfigurer<H>.AbstractInterceptUrlRegistry<ExpressionInterceptUrlRegistry, AuthorizedUrl> {
private ExpressionInterceptUrlRegistry(ApplicationContext context) {
setApplicationContext(context);
}
@Override
protected AuthorizedUrl chainRequestMatchersInternal(List<RequestMatcher> requestMatchers) {
return new AuthorizedUrl(requestMatchers);
}
/**
* Allows customization of the {@link SecurityExpressionHandler} to be used. The
* default is {@link DefaultWebSecurityExpressionHandler}
* @param expressionHandler the {@link SecurityExpressionHandler} to be used
* @return the {@link ExpressionUrlAuthorizationConfigurer} for further
* customization.
*/
public ExpressionInterceptUrlRegistry expressionHandler(
SecurityExpressionHandler<FilterInvocation> expressionHandler) {
ExpressionUrlAuthorizationConfigurer.this.expressionHandler = expressionHandler;
return this;
}
/**
* Adds an {@link ObjectPostProcessor} for this class.
* @param objectPostProcessor
* @return the {@link ExpressionUrlAuthorizationConfigurer} for further
* customizations
*/
public ExpressionInterceptUrlRegistry withObjectPostProcessor(ObjectPostProcessor<?> objectPostProcessor) {
addObjectPostProcessor(objectPostProcessor);
return this;
}
public H and() {
return ExpressionUrlAuthorizationConfigurer.this.getBuilder();
}
}
public class AuthorizedUrl {
private List<? extends RequestMatcher> requestMatchers;
private boolean not;
/**
* Creates a new instance
* @param requestMatchers the {@link RequestMatcher} instances to map
*/
AuthorizedUrl(List<? extends RequestMatcher> requestMatchers) {
this.requestMatchers = requestMatchers;
}
protected List<? extends RequestMatcher> getMatchers() {
return this.requestMatchers;
}
/**
* Negates the following expression.
* @return the {@link ExpressionUrlAuthorizationConfigurer} for further
* customization
*/
public AuthorizedUrl not() {
this.not = true;
return this;
}
/**
* Shortcut for specifying URLs require a particular role. If you do not want to
* have role prefix (default "ROLE_") automatically inserted see
* {@link #hasAuthority(String)}.
* @param role the role to require (i.e. USER, ADMIN, etc). Note, it should not
* start with role prefix as this is automatically inserted.
* @return the {@link ExpressionUrlAuthorizationConfigurer} for further
* customization
*/
public ExpressionInterceptUrlRegistry hasRole(String role) {
return access(ExpressionUrlAuthorizationConfigurer
.hasRole(ExpressionUrlAuthorizationConfigurer.this.rolePrefix, role));
}
/**
* Shortcut for specifying URLs require any of a number of roles. If you do not
* want to have role prefix (default "ROLE_") automatically inserted see
* {@link #hasAnyAuthority(String...)}
* @param roles the roles to require (i.e. USER, ADMIN, etc). Note, it should not
* start with role prefix as this is automatically inserted.
* @return the {@link ExpressionUrlAuthorizationConfigurer} for further
* customization
*/
public ExpressionInterceptUrlRegistry hasAnyRole(String... roles) {
return access(ExpressionUrlAuthorizationConfigurer
.hasAnyRole(ExpressionUrlAuthorizationConfigurer.this.rolePrefix, roles));
}
/**
* Specify that URLs require a particular authority.
* @param authority the authority to require (i.e. ROLE_USER, ROLE_ADMIN, etc).
* @return the {@link ExpressionUrlAuthorizationConfigurer} for further
* customization
*/
public ExpressionInterceptUrlRegistry hasAuthority(String authority) {
return access(ExpressionUrlAuthorizationConfigurer.hasAuthority(authority));
}
/**
* Specify that URLs requires any of a number authorities.
* @param authorities the requests require at least one of the authorities (i.e.
* "ROLE_USER","ROLE_ADMIN" would mean either "ROLE_USER" or "ROLE_ADMIN" is
* required).
* @return the {@link ExpressionUrlAuthorizationConfigurer} for further
* customization
*/
public ExpressionInterceptUrlRegistry hasAnyAuthority(String... authorities) {
return access(ExpressionUrlAuthorizationConfigurer.hasAnyAuthority(authorities));
}
/**
* Specify that URLs requires a specific IP Address or subnet.
* @param ipaddressExpression the ipaddress (i.e. 192.168.1.79) or local subnet
* (i.e. 192.168.0/24)
* @return the {@link ExpressionUrlAuthorizationConfigurer} for further
* customization
*/
public ExpressionInterceptUrlRegistry hasIpAddress(String ipaddressExpression) {
return access(ExpressionUrlAuthorizationConfigurer.hasIpAddress(ipaddressExpression));
}
/**
* Specify that URLs are allowed by anyone.
* @return the {@link ExpressionUrlAuthorizationConfigurer} for further
* customization
*/
public ExpressionInterceptUrlRegistry permitAll() {
return access(permitAll);
}
/**
* Specify that URLs are allowed by anonymous users.
* @return the {@link ExpressionUrlAuthorizationConfigurer} for further
* customization
*/
public ExpressionInterceptUrlRegistry anonymous() {
return access(anonymous);
}
/**
* Specify that URLs are allowed by users that have been remembered.
* @return the {@link ExpressionUrlAuthorizationConfigurer} for further
* customization
* @see RememberMeConfigurer
*/
public ExpressionInterceptUrlRegistry rememberMe() {
return access(rememberMe);
}
/**
* Specify that URLs are not allowed by anyone.
* @return the {@link ExpressionUrlAuthorizationConfigurer} for further
* customization
*/
public ExpressionInterceptUrlRegistry denyAll() {
return access(denyAll);
}
/**
* Specify that URLs are allowed by any authenticated user.
* @return the {@link ExpressionUrlAuthorizationConfigurer} for further
* customization
*/
public ExpressionInterceptUrlRegistry authenticated() {
return access(authenticated);
}
/**
* Specify that URLs are allowed by users who have authenticated and were not
* "remembered".
* @return the {@link ExpressionUrlAuthorizationConfigurer} for further
* customization
* @see RememberMeConfigurer
*/
public ExpressionInterceptUrlRegistry fullyAuthenticated() {
return access(fullyAuthenticated);
}
/**
* Allows specifying that URLs are secured by an arbitrary expression
* @param attribute the expression to secure the URLs (i.e. "hasRole('ROLE_USER')
* and hasRole('ROLE_SUPER')")
* @return the {@link ExpressionUrlAuthorizationConfigurer} for further
* customization
*/
public ExpressionInterceptUrlRegistry access(String attribute) {
if (this.not) {
attribute = "!" + attribute;
}
interceptUrl(this.requestMatchers, SecurityConfig.createList(attribute));
return ExpressionUrlAuthorizationConfigurer.this.REGISTRY;
}
}
}

View File

@ -18,10 +18,8 @@ package org.springframework.security.config.annotation.web.configurers;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.security.access.SecurityConfig;
import org.springframework.security.authorization.SingleResultAuthorizationManager;
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
import org.springframework.security.config.annotation.web.configurers.AbstractConfigAttributeRequestMatcherRegistry.UrlMapping;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.util.Assert;
@ -47,25 +45,14 @@ final class PermitAllSupport {
@SuppressWarnings("unchecked")
static void permitAll(HttpSecurityBuilder<? extends HttpSecurityBuilder<?>> http,
RequestMatcher... requestMatchers) {
ExpressionUrlAuthorizationConfigurer<?> configurer = http
.getConfigurer(ExpressionUrlAuthorizationConfigurer.class);
AuthorizeHttpRequestsConfigurer<?> httpConfigurer = http.getConfigurer(AuthorizeHttpRequestsConfigurer.class);
boolean oneConfigurerPresent = configurer == null ^ httpConfigurer == null;
Assert.state(oneConfigurerPresent,
"permitAll only works with either HttpSecurity.authorizeRequests() or HttpSecurity.authorizeHttpRequests(). "
+ "Please define one or the other but not both.");
Assert.state(httpConfigurer != null,
"permitAll only works with HttpSecurity.authorizeHttpRequests(). Please define one.");
for (RequestMatcher matcher : requestMatchers) {
if (matcher != null) {
if (configurer != null) {
configurer.getRegistry()
.addMapping(0, new UrlMapping(matcher,
SecurityConfig.createList(ExpressionUrlAuthorizationConfigurer.permitAll)));
}
else {
httpConfigurer.addFirst(matcher, SingleResultAuthorizationManager.permitAll());
}
httpConfigurer.addFirst(matcher, SingleResultAuthorizationManager.permitAll());
}
}
}

View File

@ -1,333 +0,0 @@
/*
* Copyright 2002-2025 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.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.springframework.context.ApplicationContext;
import org.springframework.http.HttpMethod;
import org.springframework.security.access.AccessDecisionManager;
import org.springframework.security.access.AccessDecisionVoter;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.SecurityConfig;
import org.springframework.security.access.vote.AuthenticatedVoter;
import org.springframework.security.access.vote.RoleVoter;
import org.springframework.security.config.ObjectPostProcessor;
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
import org.springframework.security.web.access.intercept.DefaultFilterInvocationSecurityMetadataSource;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.util.Assert;
/**
* Adds URL based authorization using
* {@link DefaultFilterInvocationSecurityMetadataSource}. At least one
* {@link org.springframework.web.bind.annotation.RequestMapping} needs to be mapped to
* {@link ConfigAttribute}'s for this {@link SecurityContextConfigurer} to have meaning.
* <h2>Security Filters</h2>
*
* <p>
* Usage includes applying the {@link UrlAuthorizationConfigurer} and then modifying the
* StandardInterceptUrlRegistry. For example:
* </p>
*
* <pre>
* &#64;Bean
* public SecurityFilterChain filterChain(HttpSecurity http, ApplicationContext context) throws Exception {
* http.apply(new UrlAuthorizationConfigurer&lt;HttpSecurity&gt;(context)).getRegistry()
* .requestMatchers(&quot;/users**&quot;, &quot;/sessions/**&quot;).hasRole(&quot;USER&quot;)
* .requestMatchers(&quot;/signup&quot;).hasRole(&quot;ANONYMOUS&quot;).anyRequest().hasRole(&quot;USER&quot;);
* }
* </pre>
*
* The following Filters are populated
*
* <ul>
* <li>
* {@link org.springframework.security.web.access.intercept.FilterSecurityInterceptor}</li>
* </ul>
*
* <h2>Shared Objects Created</h2>
*
* The following shared objects are populated to allow other
* {@link org.springframework.security.config.annotation.SecurityConfigurer}'s to
* customize:
* <ul>
* <li>
* {@link org.springframework.security.web.access.intercept.FilterSecurityInterceptor}</li>
* </ul>
*
* <h2>Shared Objects Used</h2>
*
* The following shared objects are used:
*
* <ul>
* <li>AuthenticationManager</li>
* </ul>
*
* @param <H> the type of {@link HttpSecurityBuilder} that is being configured
* @author Rob Winch
* @since 3.2
* @see ExpressionUrlAuthorizationConfigurer
* @deprecated Use {@link AuthorizeHttpRequestsConfigurer} instead
*/
@Deprecated
public final class UrlAuthorizationConfigurer<H extends HttpSecurityBuilder<H>>
extends AbstractInterceptUrlConfigurer<UrlAuthorizationConfigurer<H>, H> {
private final StandardInterceptUrlRegistry registry;
public UrlAuthorizationConfigurer(ApplicationContext context) {
this.registry = new StandardInterceptUrlRegistry(context);
}
/**
* The StandardInterceptUrlRegistry is what users will interact with after applying
* the {@link UrlAuthorizationConfigurer}.
* @return the {@link ExpressionUrlAuthorizationConfigurer} for further customizations
*/
public StandardInterceptUrlRegistry getRegistry() {
return this.registry;
}
/**
* Adds an {@link ObjectPostProcessor} for this class.
* @param objectPostProcessor
* @return the {@link UrlAuthorizationConfigurer} for further customizations
*/
@Override
public UrlAuthorizationConfigurer<H> withObjectPostProcessor(ObjectPostProcessor<?> objectPostProcessor) {
addObjectPostProcessor(objectPostProcessor);
return this;
}
/**
* Creates the default {@link AccessDecisionVoter} instances used if an
* {@link AccessDecisionManager} was not specified.
* @param http the builder to use
*/
@Override
@SuppressWarnings("rawtypes")
List<AccessDecisionVoter<?>> getDecisionVoters(H http) {
List<AccessDecisionVoter<?>> decisionVoters = new ArrayList<>();
decisionVoters.add(new RoleVoter());
decisionVoters.add(new AuthenticatedVoter());
return decisionVoters;
}
/**
* Creates the {@link FilterInvocationSecurityMetadataSource} to use. The
* implementation is a {@link DefaultFilterInvocationSecurityMetadataSource}.
* @param http the builder to use
*/
@Override
FilterInvocationSecurityMetadataSource createMetadataSource(H http) {
return new DefaultFilterInvocationSecurityMetadataSource(this.registry.createRequestMap());
}
/**
* Adds a mapping of the {@link RequestMatcher} instances to the
* {@link ConfigAttribute} instances.
* @param requestMatchers the {@link RequestMatcher} instances that should map to the
* provided {@link ConfigAttribute} instances
* @param configAttributes the {@link ConfigAttribute} instances that should be mapped
* by the {@link RequestMatcher} instances
* @return the {@link ExpressionUrlAuthorizationConfigurer} for further customizations
*/
private StandardInterceptUrlRegistry addMapping(Iterable<? extends RequestMatcher> requestMatchers,
Collection<ConfigAttribute> configAttributes) {
for (RequestMatcher requestMatcher : requestMatchers) {
this.registry.addMapping(
new AbstractConfigAttributeRequestMatcherRegistry.UrlMapping(requestMatcher, configAttributes));
}
return this.registry;
}
/**
* Creates a String for specifying a user requires a role.
* @param role the role that should be required which is prepended with ROLE_
* automatically (i.e. USER, ADMIN, etc). It should not start with ROLE_
* @return the {@link ConfigAttribute} expressed as a String
*/
private static String hasRole(String role) {
Assert.isTrue(!role.startsWith("ROLE_"), () -> role
+ " should not start with ROLE_ since ROLE_ is automatically prepended when using hasRole. Consider using hasAuthority or access instead.");
return "ROLE_" + role;
}
/**
* Creates a String for specifying that a user requires one of many roles.
* @param roles the roles that the user should have at least one of (i.e. ADMIN, USER,
* etc). Each role should not start with ROLE_ since it is automatically prepended
* already.
* @return the {@link ConfigAttribute} expressed as a String
*/
private static String[] hasAnyRole(String... roles) {
for (int i = 0; i < roles.length; i++) {
roles[i] = "ROLE_" + roles[i];
}
return roles;
}
/**
* Creates a String for specifying that a user requires one of many authorities
* @param authorities the authorities that the user should have at least one of (i.e.
* ROLE_USER, ROLE_ADMIN, etc).
* @return the {@link ConfigAttribute} expressed as a String.
*/
private static String[] hasAnyAuthority(String... authorities) {
return authorities;
}
@Deprecated
public final class StandardInterceptUrlRegistry extends
UrlAuthorizationConfigurer<H>.AbstractInterceptUrlRegistry<StandardInterceptUrlRegistry, AuthorizedUrl> {
private StandardInterceptUrlRegistry(ApplicationContext context) {
setApplicationContext(context);
}
@Override
public AuthorizedUrl requestMatchers(String... patterns) {
return super.requestMatchers(patterns);
}
@Override
public AuthorizedUrl requestMatchers(HttpMethod method, String... patterns) {
return super.requestMatchers(method, patterns);
}
@Override
public AuthorizedUrl requestMatchers(HttpMethod method) {
return super.requestMatchers(method);
}
@Override
public AuthorizedUrl requestMatchers(RequestMatcher... requestMatchers) {
return super.requestMatchers(requestMatchers);
}
@Override
protected AuthorizedUrl chainRequestMatchersInternal(List<RequestMatcher> requestMatchers) {
return new AuthorizedUrl(requestMatchers);
}
/**
* Adds an {@link ObjectPostProcessor} for this class.
* @param objectPostProcessor
* @return the {@link ExpressionUrlAuthorizationConfigurer} for further
* customizations
*/
public StandardInterceptUrlRegistry withObjectPostProcessor(ObjectPostProcessor<?> objectPostProcessor) {
addObjectPostProcessor(objectPostProcessor);
return this;
}
public H and() {
return UrlAuthorizationConfigurer.this.getBuilder();
}
}
/**
* Maps the specified {@link RequestMatcher} instances to {@link ConfigAttribute}
* instances.
*
* @author Rob Winch
* @since 3.2
*/
public class AuthorizedUrl {
private final List<? extends RequestMatcher> requestMatchers;
/**
* Creates a new instance
* @param requestMatchers the {@link RequestMatcher} instances to map to some
* {@link ConfigAttribute} instances.
*/
AuthorizedUrl(List<? extends RequestMatcher> requestMatchers) {
Assert.notEmpty(requestMatchers, "requestMatchers must contain at least one value");
this.requestMatchers = requestMatchers;
}
/**
* Specifies a user requires a role.
* @param role the role that should be required which is prepended with ROLE_
* automatically (i.e. USER, ADMIN, etc). It should not start with ROLE_ the
* {@link UrlAuthorizationConfigurer} for further customization
*/
public StandardInterceptUrlRegistry hasRole(String role) {
return access(UrlAuthorizationConfigurer.hasRole(role));
}
/**
* Specifies that a user requires one of many roles.
* @param roles the roles that the user should have at least one of (i.e. ADMIN,
* USER, etc). Each role should not start with ROLE_ since it is automatically
* prepended already.
* @return the {@link UrlAuthorizationConfigurer} for further customization
*/
public StandardInterceptUrlRegistry hasAnyRole(String... roles) {
return access(UrlAuthorizationConfigurer.hasAnyRole(roles));
}
/**
* Specifies a user requires an authority.
* @param authority the authority that should be required
* @return the {@link UrlAuthorizationConfigurer} for further customization
*/
public StandardInterceptUrlRegistry hasAuthority(String authority) {
return access(authority);
}
/**
* Specifies that a user requires one of many authorities
* @param authorities the authorities that the user should have at least one of
* (i.e. ROLE_USER, ROLE_ADMIN, etc).
* @return the {@link UrlAuthorizationConfigurer} for further customization
*/
public StandardInterceptUrlRegistry hasAnyAuthority(String... authorities) {
return access(UrlAuthorizationConfigurer.hasAnyAuthority(authorities));
}
/**
* Specifies that an anonymous user is allowed access
* @return the {@link UrlAuthorizationConfigurer} for further customization
*/
public StandardInterceptUrlRegistry anonymous() {
return hasRole("ANONYMOUS");
}
/**
* Specifies that the user must have the specified {@link ConfigAttribute}'s
* @param attributes the {@link ConfigAttribute}'s that restrict access to a URL
* @return the {@link UrlAuthorizationConfigurer} for further customization
*/
public StandardInterceptUrlRegistry access(String... attributes) {
addMapping(this.requestMatchers, SecurityConfig.createList(attributes));
return UrlAuthorizationConfigurer.this.registry;
}
protected List<? extends RequestMatcher> getMatchers() {
return this.requestMatchers;
}
}
}

View File

@ -1,234 +0,0 @@
/*
* 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
import org.springframework.http.HttpMethod
import org.springframework.security.config.annotation.web.builders.HttpSecurity
import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer
import org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher
import org.springframework.security.web.util.matcher.AnyRequestMatcher
import org.springframework.security.web.util.matcher.RequestMatcher
/**
* A Kotlin DSL to configure [HttpSecurity] request authorization using idiomatic Kotlin code.
*
* @author Eleftheria Stein
* @since 5.3
*/
class AuthorizeRequestsDsl : AbstractRequestMatcherDsl() {
private val authorizationRules = mutableListOf<AuthorizationRule>()
private val PATTERN_TYPE = PatternType.PATH;
/**
* Adds a request authorization rule.
*
* @param matches the [RequestMatcher] to match incoming requests against
* @param access the SpEL expression to secure the matching request
* (i.e. "hasAuthority('ROLE_USER') and hasAuthority('ROLE_SUPER')")
*/
fun authorize(matches: RequestMatcher = AnyRequestMatcher.INSTANCE,
access: String) {
authorizationRules.add(MatcherAuthorizationRule(matches, access))
}
/**
* Adds a request authorization rule for an endpoint matching the provided
* pattern.
* If Spring MVC is on the classpath, it will use an MVC matcher.
* If Spring MVC is not on the classpath, it will use an ant matcher.
* The MVC 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 current request will not be processed by Spring MVC, a reasonable default
* using the pattern as an ant pattern will be used.
*
* @param pattern the pattern to match incoming requests against.
* @param access the SpEL expression to secure the matching request
* (i.e. "hasAuthority('ROLE_USER') and hasAuthority('ROLE_SUPER')")
*/
fun authorize(pattern: String, access: String) {
authorizationRules.add(PatternAuthorizationRule(pattern = pattern,
patternType = PATTERN_TYPE,
rule = access))
}
/**
* Adds a request authorization rule for an endpoint matching the provided
* pattern.
* If Spring MVC is on the classpath, it will use an MVC matcher.
* If Spring MVC is not on the classpath, it will use an ant matcher.
* The MVC 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 current request will not be processed by Spring MVC, a reasonable default
* using the pattern as an ant pattern will be used.
*
* @param method the HTTP method to match the income requests against.
* @param pattern the pattern to match incoming requests against.
* @param access the SpEL expression to secure the matching request
* (i.e. "hasAuthority('ROLE_USER') and hasAuthority('ROLE_SUPER')")
*/
fun authorize(method: HttpMethod, pattern: String, access: String) {
authorizationRules.add(PatternAuthorizationRule(pattern = pattern,
patternType = PATTERN_TYPE,
httpMethod = method,
rule = access))
}
/**
* Adds a request authorization rule for an endpoint matching the provided
* pattern.
* If Spring MVC is on the classpath, it will use an MVC matcher.
* If Spring MVC is not on the classpath, it will use an ant matcher.
* The MVC 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 current request will not be processed by Spring MVC, a reasonable default
* using the pattern as an ant pattern will be used.
*
* @param pattern the pattern to match incoming requests against.
* @param servletPath the servlet path to match incoming requests against. This
* only applies when using an MVC pattern matcher.
* @param access the SpEL expression to secure the matching request
* (i.e. "hasAuthority('ROLE_USER') and hasAuthority('ROLE_SUPER')")
*/
fun authorize(pattern: String, servletPath: String, access: String) {
authorizationRules.add(PatternAuthorizationRule(pattern = pattern,
patternType = PATTERN_TYPE,
servletPath = servletPath,
rule = access))
}
/**
* Adds a request authorization rule for an endpoint matching the provided
* pattern.
* If Spring MVC is on the classpath, it will use an MVC matcher.
* If Spring MVC is not on the classpath, it will use an ant matcher.
* The MVC 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 current request will not be processed by Spring MVC, a reasonable default
* using the pattern as an ant pattern will be used.
*
* @param method the HTTP method to match the income requests against.
* @param pattern the pattern to match incoming requests against.
* @param servletPath the servlet path to match incoming requests against. This
* only applies when using an MVC pattern matcher.
* @param access the SpEL expression to secure the matching request
* (i.e. "hasAuthority('ROLE_USER') and hasAuthority('ROLE_SUPER')")
*/
fun authorize(method: HttpMethod, pattern: String, servletPath: String, access: String) {
authorizationRules.add(PatternAuthorizationRule(pattern = pattern,
patternType = PATTERN_TYPE,
servletPath = servletPath,
httpMethod = method,
rule = access))
}
/**
* Specify that URLs require a particular authority.
*
* @param authority the authority to require (i.e. ROLE_USER, ROLE_ADMIN, etc).
* @return the SpEL expression "hasAuthority" with the given authority as a
* parameter
*/
fun hasAuthority(authority: String) = "hasAuthority('$authority')"
/**
* Specify that URLs require any number of authorities.
*
* @param authorities the authorities to require (i.e. ROLE_USER, ROLE_ADMIN, etc).
* @return the SpEL expression "hasAnyAuthority" with the given authorities as a
* parameter
*/
fun hasAnyAuthority(vararg authorities: String): String {
val anyAuthorities = authorities.joinToString("','")
return "hasAnyAuthority('$anyAuthorities')"
}
/**
* Specify that URLs require a particular role.
*
* @param role the role to require (i.e. USER, ADMIN, etc).
* @return the SpEL expression "hasRole" with the given role as a
* parameter
*/
fun hasRole(role: String) = "hasRole('$role')"
/**
* Specify that URLs require any number of roles.
*
* @param roles the roles to require (i.e. USER, ADMIN, etc).
* @return the SpEL expression "hasAnyRole" with the given roles as a
* parameter
*/
fun hasAnyRole(vararg roles: String): String {
val anyRoles = roles.joinToString("','")
return "hasAnyRole('$anyRoles')"
}
/**
* Specify that URLs are allowed by anyone.
*/
val permitAll = "permitAll"
/**
* Specify that URLs are allowed by anonymous users.
*/
val anonymous = "anonymous"
/**
* Specify that URLs are allowed by users that have been remembered.
*/
val rememberMe = "rememberMe"
/**
* Specify that URLs are not allowed by anyone.
*/
val denyAll = "denyAll"
/**
* Specify that URLs are allowed by any authenticated user.
*/
val authenticated = "authenticated"
/**
* Specify that URLs are allowed by users who have authenticated and were not
* "remembered".
*/
val fullyAuthenticated = "fullyAuthenticated"
internal fun get(): (ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry) -> Unit {
return { requests ->
authorizationRules.forEach { rule ->
when (rule) {
is MatcherAuthorizationRule -> requests.requestMatchers(rule.matcher).access(rule.rule)
is PatternAuthorizationRule -> {
var builder = requests.applicationContext.getBeanProvider(
PathPatternRequestMatcher.Builder::class.java)
.getIfUnique(PathPatternRequestMatcher::withDefaults);
if (rule.servletPath != null) {
builder = builder.basePath(rule.servletPath)
}
requests.requestMatchers(builder.matcher(rule.httpMethod, rule.pattern)).access(rule.rule)
}
}
}
}
}
}

View File

@ -241,39 +241,6 @@ class HttpSecurityDsl(private val http: HttpSecurity, private val init: HttpSecu
this.http.formLogin(loginCustomizer)
}
/**
* Allows restricting access based upon the [HttpServletRequest]
*
* Example:
*
* ```
* @Configuration
* @EnableWebSecurity
* class SecurityConfig {
*
* @Bean
* fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
* http {
* authorizeRequests {
* authorize("/public", permitAll)
* authorize(anyRequest, authenticated)
* }
* }
* return http.build()
* }
* }
* ```
*
* @param authorizeRequestsConfiguration custom configuration that specifies
* access for requests
* @see [AuthorizeRequestsDsl]
*/
@Deprecated(message = "Since 6.4. Use authorizeHttpRequests instead")
fun authorizeRequests(authorizeRequestsConfiguration: AuthorizeRequestsDsl.() -> Unit) {
val authorizeRequestsCustomizer = AuthorizeRequestsDsl().apply(authorizeRequestsConfiguration).get()
this.http.authorizeRequests(authorizeRequestsCustomizer)
}
/**
* Allows restricting access based upon the [HttpServletRequest]
*

View File

@ -1,561 +0,0 @@
/*
* Copyright 2002-2025 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 jakarta.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.http.HttpMethod;
import org.springframework.mock.web.MockFilterChain;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.security.access.hierarchicalroles.RoleHierarchy;
import org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextImpl;
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.access.expression.WebExpressionAuthorizationManager;
import org.springframework.security.web.context.HttpSessionSecurityContextRepository;
import org.springframework.security.web.servlet.MockServletContext;
import org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;
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.util.pattern.PathPatternParser;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.spy;
import static org.springframework.security.config.Customizer.withDefaults;
import static org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher.pathPattern;
/**
* @author Rob Winch
*
*/
public class AuthorizeRequestsTests {
AnnotationConfigWebApplicationContext context;
MockHttpServletRequest request;
MockHttpServletResponse response;
MockFilterChain chain;
MockServletContext servletContext;
@Autowired
FilterChainProxy springSecurityFilterChain;
@BeforeEach
public void setup() {
this.servletContext = spy(MockServletContext.mvc());
this.request = new MockHttpServletRequest(this.servletContext, "GET", "");
this.response = new MockHttpServletResponse();
this.chain = new MockFilterChain();
}
@AfterEach
public void cleanup() {
if (this.context != null) {
this.context.close();
}
}
// SEC-3135
@Test
public void antMatchersMethodAndNoPatterns() throws Exception {
loadConfig(AntMatchersNoPatternsConfig.class);
this.request.setMethod("POST");
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_FORBIDDEN);
}
@Test
public void postWhenPostDenyAllInLambdaThenRespondsWithForbidden() throws Exception {
loadConfig(AntMatchersNoPatternsInLambdaConfig.class);
this.request.setMethod("POST");
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_FORBIDDEN);
}
// SEC-2256
@Test
public void antMatchersPathVariables() throws Exception {
loadConfig(AntPatchersPathVariables.class);
this.request.setServletPath("/user/user");
this.request.setRequestURI("/user/user");
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_OK);
this.setup();
this.request.setServletPath("/user/deny");
this.request.setRequestURI("/user/deny");
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_FORBIDDEN);
}
// SEC-2256
@Test
public void antMatchersPathVariablesCaseInsensitive() throws Exception {
loadConfig(AntPatchersPathVariables.class);
this.request.setRequestURI("/USER/user");
this.request.setServletPath("/USER/user");
this.request.setRequestURI("/USER/user");
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_OK);
this.setup();
this.request.setRequestURI("/USER/deny");
this.request.setServletPath("/USER/deny");
this.request.setRequestURI("/USER/deny");
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_FORBIDDEN);
}
// gh-3786
@Test
public void antMatchersPathVariablesCaseInsensitiveCamelCaseVariables() throws Exception {
loadConfig(AntMatchersPathVariablesCamelCaseVariables.class);
this.request.setServletPath("/USER/user");
this.request.setRequestURI("/USER/user");
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_OK);
this.setup();
this.request.setServletPath("/USER/deny");
this.request.setRequestURI("/USER/deny");
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_FORBIDDEN);
}
// gh-3394
@Test
public void roleHiearchy() throws Exception {
loadConfig(RoleHiearchyConfig.class);
SecurityContext securityContext = new SecurityContextImpl();
securityContext.setAuthentication(UsernamePasswordAuthenticationToken.authenticated("test", "notused",
AuthorityUtils.createAuthorityList("ROLE_USER")));
this.request.getSession()
.setAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY, securityContext);
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_OK);
}
@Test
public void mvcMatcherPathVariables() throws Exception {
loadConfig(MvcMatcherPathVariablesConfig.class);
this.request.setRequestURI("/user/user");
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_OK);
this.setup();
this.request.setRequestURI("/user/deny");
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_UNAUTHORIZED);
}
@Test
public void requestWhenMvcMatcherPathVariablesThenMatchesOnPathVariables() throws Exception {
loadConfig(MvcMatcherPathVariablesInLambdaConfig.class);
this.request.setRequestURI("/user/user");
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_OK);
this.setup();
this.request.setRequestURI("/user/deny");
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(this.servletContext);
this.context.refresh();
this.context.getAutowireCapableBeanFactory().autowireBean(this);
}
@EnableWebSecurity
@Configuration
static class AntMatchersNoPatternsConfig {
@Bean
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
// @formatter:off
http
.authorizeHttpRequests((requests) -> requests
.requestMatchers(pathPattern(HttpMethod.POST, "/**")).denyAll());
// @formatter:on
return http.build();
}
@Bean
UserDetailsService userDetailsService() {
return new InMemoryUserDetailsManager();
}
}
@EnableWebSecurity
@Configuration
static class AntMatchersNoPatternsInLambdaConfig {
@Bean
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
// @formatter:off
http
.authorizeHttpRequests((authorize) -> authorize
.requestMatchers(pathPattern(HttpMethod.POST, "/**")).denyAll()
);
// @formatter:on
return http.build();
}
@Bean
UserDetailsService userDetailsService() {
return new InMemoryUserDetailsManager();
}
}
@EnableWebSecurity
@Configuration
static class AntPatchersPathVariables {
@Bean
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
PathPatternParser parser = new PathPatternParser();
parser.setCaseSensitive(false);
PathPatternRequestMatcher.Builder builder = PathPatternRequestMatcher.withPathPatternParser(parser);
WebExpressionAuthorizationManager authz = new WebExpressionAuthorizationManager("#user == 'user'");
// @formatter:off
http
.authorizeHttpRequests((requests) -> requests
.requestMatchers(builder.matcher("/user/{user}")).access(authz)
.anyRequest().denyAll());
// @formatter:on
return http.build();
}
@Bean
UserDetailsService userDetailsService() {
return new InMemoryUserDetailsManager();
}
}
@EnableWebSecurity
@Configuration
static class AntMatchersPathVariablesCamelCaseVariables {
@Bean
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
PathPatternParser parser = new PathPatternParser();
parser.setCaseSensitive(false);
PathPatternRequestMatcher.Builder builder = PathPatternRequestMatcher.withPathPatternParser(parser);
WebExpressionAuthorizationManager authz = new WebExpressionAuthorizationManager("#userName == 'user'");
// @formatter:off
http
.authorizeHttpRequests((requests) -> requests
.requestMatchers(builder.matcher("/user/{userName}")).access(authz)
.anyRequest().denyAll());
// @formatter:on
return http.build();
}
@Bean
UserDetailsService userDetailsService() {
return new InMemoryUserDetailsManager();
}
}
@EnableWebSecurity
@Configuration
static class RoleHiearchyConfig {
@Bean
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
// @formatter:off
http
.authorizeHttpRequests((requests) -> requests
.anyRequest().hasRole("ADMIN"));
// @formatter:on
return http.build();
}
@Bean
UserDetailsService userDetailsService() {
return new InMemoryUserDetailsManager();
}
@Bean
RoleHierarchy roleHiearchy() {
return RoleHierarchyImpl.fromHierarchy("ROLE_USER > ROLE_ADMIN");
}
}
@EnableWebSecurity
@Configuration
@EnableWebMvc
static class MvcMatcherConfig {
@Bean
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
// @formatter:off
http
.httpBasic(withDefaults())
.authorizeHttpRequests((requests) -> requests
.requestMatchers("/path").denyAll());
// @formatter:on
return http.build();
}
@Bean
UserDetailsService userDetailsService() {
return new InMemoryUserDetailsManager();
}
@RestController
static class PathController {
@RequestMapping("/path")
String path() {
return "path";
}
}
}
@EnableWebSecurity
@Configuration
@EnableWebMvc
static class MvcMatcherInLambdaConfig {
@Bean
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
// @formatter:off
http
.httpBasic(withDefaults())
.authorizeHttpRequests((authorize) -> authorize
.requestMatchers("/path").denyAll()
);
// @formatter:on
return http.build();
}
@Bean
UserDetailsService userDetailsService() {
return new InMemoryUserDetailsManager();
}
@RestController
static class PathController {
@RequestMapping("/path")
String path() {
return "path";
}
}
}
@EnableWebSecurity
@Configuration
@EnableWebMvc
static class MvcMatcherServletPathConfig {
@Bean
SecurityFilterChain filterChain(HttpSecurity http, PathPatternRequestMatcher.Builder builder) throws Exception {
PathPatternRequestMatcher.Builder spring = builder.basePath("/spring");
// @formatter:off
http
.httpBasic(withDefaults())
.authorizeHttpRequests((requests) -> requests
.requestMatchers(spring.matcher("/path")).denyAll());
// @formatter:on
return http.build();
}
@Bean
UserDetailsService userDetailsService() {
return new InMemoryUserDetailsManager();
}
@RestController
static class PathController {
@RequestMapping("/path")
String path() {
return "path";
}
}
}
@EnableWebSecurity
@Configuration
@EnableWebMvc
static class MvcMatcherServletPathInLambdaConfig {
@Bean
SecurityFilterChain filterChain(HttpSecurity http, PathPatternRequestMatcher.Builder builder) throws Exception {
PathPatternRequestMatcher.Builder spring = builder.basePath("/spring");
// @formatter:off
http
.httpBasic(withDefaults())
.authorizeHttpRequests((authorize) -> authorize
.requestMatchers(spring.matcher("/path")).denyAll()
);
// @formatter:on
return http.build();
}
@Bean
UserDetailsService userDetailsService() {
return new InMemoryUserDetailsManager();
}
@RestController
static class PathController {
@RequestMapping("/path")
String path() {
return "path";
}
}
}
@EnableWebSecurity
@Configuration
@EnableWebMvc
static class MvcMatcherPathVariablesConfig {
@Bean
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
WebExpressionAuthorizationManager authz = new WebExpressionAuthorizationManager("#userName == 'user'");
// @formatter:off
http
.httpBasic(withDefaults())
.authorizeHttpRequests((requests) -> requests
.requestMatchers("/user/{userName}").access(authz));
// @formatter:on
return http.build();
}
@Bean
UserDetailsService userDetailsService() {
return new InMemoryUserDetailsManager();
}
@RestController
static class PathController {
@RequestMapping("/path")
String path() {
return "path";
}
}
}
@EnableWebSecurity
@Configuration
@EnableWebMvc
static class MvcMatcherPathVariablesInLambdaConfig {
@Bean
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
WebExpressionAuthorizationManager authz = new WebExpressionAuthorizationManager("#userName == 'user'");
// @formatter:off
http
.httpBasic(withDefaults())
.authorizeHttpRequests((authorize) -> authorize
.requestMatchers("/user/{userName}").access(authz)
);
// @formatter:on
return http.build();
}
@Bean
UserDetailsService userDetailsService() {
return new InMemoryUserDetailsManager();
}
@RestController
static class PathController {
@RequestMapping("/path")
String path() {
return "path";
}
}
}
@EnableWebSecurity
@Configuration
@EnableWebMvc
static class MvcMatcherPathServletPathRequiredConfig {
@Bean
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
// @formatter:off
http
.httpBasic(withDefaults())
.authorizeHttpRequests((requests) -> requests
.requestMatchers("/user").denyAll());
// @formatter:on
return http.build();
}
@Bean
UserDetailsService userDetailsService() {
return new InMemoryUserDetailsManager();
}
@RestController
static class PathController {
@RequestMapping("/path")
String path() {
return "path";
}
}
}
}

View File

@ -81,7 +81,7 @@ public class PermitAllSupportTests {
assertThatExceptionOfType(BeanCreationException.class)
.isThrownBy(() -> this.spring.register(NoAuthorizedUrlsConfig.class).autowire())
.withMessageContaining(
"permitAll only works with either HttpSecurity.authorizeRequests() or HttpSecurity.authorizeHttpRequests()");
"permitAll only works with HttpSecurity.authorizeHttpRequests(). Please define one.");
}
@Configuration

View File

@ -1,254 +0,0 @@
/*
* Copyright 2002-2024 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.util.Base64;
import jakarta.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.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.mock.web.MockFilterChain;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.core.userdetails.PasswordEncodedUser;
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.MockServletContext;
import org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;
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 static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.security.config.Customizer.withDefaults;
/**
* @author Rob Winch
* @author M.S. Dousti
*
*/
public class UrlAuthorizationConfigurerTests {
AnnotationConfigWebApplicationContext context;
MockHttpServletRequest request;
MockHttpServletResponse response;
MockFilterChain chain;
@Autowired
FilterChainProxy springSecurityFilterChain;
@BeforeEach
public void setup() {
this.request = new MockHttpServletRequest(MockServletContext.mvc(), "GET", "");
this.request.setMethod("GET");
this.response = new MockHttpServletResponse();
this.chain = new MockFilterChain();
}
@AfterEach
public void cleanup() {
if (this.context != null) {
this.context.close();
}
}
@Test
public void anonymousUrlAuthorization() {
loadConfig(AnonymousUrlAuthorizationConfig.class);
}
// gh-10956
@Test
public void multiMvcMatchersConfig() throws Exception {
loadConfig(MultiMvcMatcherConfig.class);
this.request.addHeader("Authorization",
"Basic " + new String(Base64.getEncoder().encode("user:password".getBytes())));
this.request.setRequestURI("/test-1");
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_FORBIDDEN);
setup();
this.request.addHeader("Authorization",
"Basic " + new String(Base64.getEncoder().encode("user:password".getBytes())));
this.request.setRequestURI("/test-2");
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_FORBIDDEN);
setup();
this.request.addHeader("Authorization",
"Basic " + new String(Base64.getEncoder().encode("user:password".getBytes())));
this.request.setRequestURI("/test-3");
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_FORBIDDEN);
setup();
this.request.addHeader("Authorization",
"Basic " + new String(Base64.getEncoder().encode("user:password".getBytes())));
this.request.setRequestURI("/test-x");
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_OK);
}
public void loadConfig(Class<?>... configs) {
this.context = new AnnotationConfigWebApplicationContext();
this.context.register(configs);
this.context.setServletContext(MockServletContext.mvc());
this.context.refresh();
this.context.getAutowireCapableBeanFactory().autowireBean(this);
}
@EnableWebSecurity
@Configuration
@EnableWebMvc
static class MvcMatcherConfig {
@Bean
SecurityFilterChain filterChain(HttpSecurity http, ApplicationContext context,
PathPatternRequestMatcher.Builder builder) throws Exception {
// @formatter:off
http
.httpBasic(withDefaults())
.apply(new UrlAuthorizationConfigurer(context)).getRegistry()
.requestMatchers(builder.matcher("/path")).hasRole("ADMIN");
// @formatter:on
return http.build();
}
@Bean
UserDetailsService userDetailsService() {
return new InMemoryUserDetailsManager(PasswordEncodedUser.user());
}
@RestController
static class PathController {
@RequestMapping("/path")
String path() {
return "path";
}
}
}
@EnableWebSecurity
@Configuration
@EnableWebMvc
static class MvcMatcherServletPathConfig {
@Bean
SecurityFilterChain filterChain(HttpSecurity http, ApplicationContext context,
PathPatternRequestMatcher.Builder builder) throws Exception {
PathPatternRequestMatcher.Builder spring = builder.basePath("/spring");
// @formatter:off
http
.httpBasic(withDefaults())
.apply(new UrlAuthorizationConfigurer(context)).getRegistry()
.requestMatchers(builder.matcher("/path")).hasRole("ADMIN");
// @formatter:on
return http.build();
}
@Bean
UserDetailsService userDetailsService() {
return new InMemoryUserDetailsManager(PasswordEncodedUser.user());
}
@RestController
static class PathController {
@RequestMapping("/path")
String path() {
return "path";
}
}
}
@EnableWebSecurity
@Configuration
static class AnonymousUrlAuthorizationConfig {
@Bean
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
// @formatter:off
http
.apply(new UrlAuthorizationConfigurer<>(null)).getRegistry()
.anyRequest().anonymous();
return http.build();
// @formatter:on
}
}
@EnableWebSecurity
@Configuration
@EnableWebMvc
static class MultiMvcMatcherConfig {
@Bean
SecurityFilterChain security(HttpSecurity http, ApplicationContext context) throws Exception {
// @formatter:off
http
.httpBasic(Customizer.withDefaults())
.apply(new UrlAuthorizationConfigurer<>(context)).getRegistry()
.requestMatchers("/test-1").hasRole("ADMIN")
.requestMatchers("/test-2").hasRole("ADMIN")
.requestMatchers("/test-3").hasRole("ADMIN")
.anyRequest().hasRole("USER");
// @formatter:on
return http.build();
}
@Bean
UserDetailsService userDetailsService() {
UserDetails user = User.withDefaultPasswordEncoder()
.username("user")
.password("password")
.roles("USER")
.build();
return new InMemoryUserDetailsManager(user);
}
@RestController
static class PathController {
@RequestMapping({ "/test-1", "/test-2", "/test-3", "/test-x" })
String path() {
return "path";
}
}
}
}

View File

@ -1,520 +0,0 @@
/*
* 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
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.http.HttpMethod
import org.springframework.security.config.annotation.web.builders.HttpSecurity
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
import org.springframework.security.config.test.SpringTestContext
import org.springframework.security.config.test.SpringTestContextExtension
import org.springframework.security.core.userdetails.User
import org.springframework.security.core.userdetails.UserDetailsService
import org.springframework.security.provisioning.InMemoryUserDetailsManager
import org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf
import org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.httpBasic
import org.springframework.security.web.SecurityFilterChain
import org.springframework.security.web.util.matcher.RegexRequestMatcher
import org.springframework.test.web.servlet.MockMvc
import org.springframework.test.web.servlet.get
import org.springframework.test.web.servlet.post
import org.springframework.test.web.servlet.put
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders
import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController
import org.springframework.web.servlet.config.annotation.EnableWebMvc
/**
* Tests for [AuthorizeRequestsDsl]
*
* @author Eleftheria Stein
*/
@ExtendWith(SpringTestContextExtension::class)
class AuthorizeRequestsDslTests {
@JvmField
val spring = SpringTestContext(this)
@Autowired
lateinit var mockMvc: MockMvc
@Test
fun `request when secured by regex matcher then responds with forbidden`() {
this.spring.register(AuthorizeRequestsByRegexConfig::class.java).autowire()
this.mockMvc.get("/private")
.andExpect {
status { isForbidden() }
}
}
@Test
fun `request when allowed by regex matcher then responds with ok`() {
this.spring.register(AuthorizeRequestsByRegexConfig::class.java).autowire()
this.mockMvc.get("/path")
.andExpect {
status { isOk() }
}
}
@Test
fun `request when allowed by regex matcher with http method then responds based on method`() {
this.spring.register(AuthorizeRequestsByRegexConfig::class.java).autowire()
this.mockMvc.post("/onlyPostPermitted") { with(csrf()) }
.andExpect {
status { isOk() }
}
this.mockMvc.get("/onlyPostPermitted")
.andExpect {
status { isForbidden() }
}
}
@Configuration
@EnableWebSecurity
open class AuthorizeRequestsByRegexConfig {
@Bean
open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
http {
authorizeRequests {
authorize(RegexRequestMatcher("/path", null), permitAll)
authorize(RegexRequestMatcher("/onlyPostPermitted", "POST"), permitAll)
authorize(RegexRequestMatcher("/onlyPostPermitted", "GET"), denyAll)
authorize(RegexRequestMatcher(".*", null), authenticated)
}
}
return http.build()
}
@RestController
internal class PathController {
@RequestMapping("/path")
fun path(): String {
return "ok"
}
@RequestMapping("/onlyPostPermitted")
fun onlyPostPermitted(): String {
return "ok"
}
}
}
@Test
fun `request when secured by mvc then responds with forbidden`() {
this.spring.register(AuthorizeRequestsByMvcConfig::class.java).autowire()
this.mockMvc.get("/private")
.andExpect {
status { isForbidden() }
}
}
@Configuration
@EnableWebSecurity
@EnableWebMvc
open class AuthorizeRequestsByMvcConfig {
@Bean
open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
http {
authorizeRequests {
authorize("/path", permitAll)
authorize("/**", authenticated)
}
}
return http.build()
}
@RestController
internal class PathController {
@RequestMapping("/path")
fun path(): String {
return "ok"
}
}
}
@Test
fun `request when secured by mvc path variables then responds based on path variable value`() {
this.spring.register(MvcMatcherPathVariablesConfig::class.java).autowire()
this.mockMvc.get("/user/user")
.andExpect {
status { isOk() }
}
this.mockMvc.get("/user/deny")
.andExpect {
status { isForbidden() }
}
}
@Configuration
@EnableWebSecurity
@EnableWebMvc
open class MvcMatcherPathVariablesConfig {
@Bean
open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
http {
authorizeRequests {
authorize("/user/{userName}", "#userName == 'user'")
}
}
return http.build()
}
@RestController
internal class PathController {
@RequestMapping("/user/{user}")
fun path(@PathVariable user: String) {
}
}
}
@Test
fun `request when user has allowed role then responds with OK`() {
this.spring.register(HasRoleConfig::class.java).autowire()
this.mockMvc.get("/") {
with(httpBasic("admin", "password"))
}.andExpect {
status { isOk() }
}
}
@Test
fun `request when user does not have allowed role then responds with forbidden`() {
this.spring.register(HasRoleConfig::class.java).autowire()
this.mockMvc.get("/") {
with(httpBasic("user", "password"))
}.andExpect {
status { isForbidden() }
}
}
@Configuration
@EnableWebSecurity
@EnableWebMvc
open class HasRoleConfig {
@Bean
open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
http {
authorizeRequests {
authorize("/**", hasRole("ADMIN"))
}
httpBasic { }
}
return http.build()
}
@RestController
internal class PathController {
@GetMapping("/")
fun index() {
}
}
@Bean
open fun userDetailsService(): UserDetailsService {
val userDetails = User.withDefaultPasswordEncoder()
.username("user")
.password("password")
.roles("USER")
.build()
val adminDetails = User.withDefaultPasswordEncoder()
.username("admin")
.password("password")
.roles("ADMIN")
.build()
return InMemoryUserDetailsManager(userDetails, adminDetails)
}
}
@Test
fun `request when user has some allowed roles then responds with OK`() {
this.spring.register(HasAnyRoleConfig::class.java).autowire()
this.mockMvc.get("/") {
with(httpBasic("user", "password"))
}.andExpect {
status { isOk() }
}
this.mockMvc.get("/") {
with(httpBasic("admin", "password"))
}.andExpect {
status { isOk() }
}
}
@Test
fun `request when user does not have any allowed roles then responds with forbidden`() {
this.spring.register(HasAnyRoleConfig::class.java).autowire()
this.mockMvc.get("/") {
with(httpBasic("other", "password"))
}.andExpect {
status { isForbidden() }
}
}
@Configuration
@EnableWebSecurity
@EnableWebMvc
open class HasAnyRoleConfig {
@Bean
open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
http {
authorizeRequests {
authorize("/**", hasAnyRole("ADMIN", "USER"))
}
httpBasic { }
}
return http.build()
}
@RestController
internal class PathController {
@GetMapping("/")
fun index():String {
return "ok"
}
}
@Bean
open fun userDetailsService(): UserDetailsService {
val userDetails = User.withDefaultPasswordEncoder()
.username("user")
.password("password")
.roles("USER")
.build()
val admin1Details = User.withDefaultPasswordEncoder()
.username("admin")
.password("password")
.roles("ADMIN")
.build()
val admin2Details = User.withDefaultPasswordEncoder()
.username("other")
.password("password")
.roles("OTHER")
.build()
return InMemoryUserDetailsManager(userDetails, admin1Details, admin2Details)
}
}
@Test
fun `request when user has some allowed authorities then responds with OK`() {
this.spring.register(HasAnyAuthorityConfig::class.java).autowire()
this.mockMvc.get("/") {
with(httpBasic("user", "password"))
}.andExpect {
status { isOk() }
}
this.mockMvc.get("/") {
with(httpBasic("admin", "password"))
}.andExpect {
status { isOk() }
}
}
@Test
fun `request when user does not have any allowed authorities then responds with forbidden`() {
this.spring.register(HasAnyAuthorityConfig::class.java).autowire()
this.mockMvc.get("/") {
with(httpBasic("other", "password"))
}.andExpect {
status { isForbidden() }
}
}
@Configuration
@EnableWebSecurity
@EnableWebMvc
open class HasAnyAuthorityConfig {
@Bean
open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
http {
authorizeRequests {
authorize("/**", hasAnyAuthority("ROLE_ADMIN", "ROLE_USER"))
}
httpBasic { }
}
return http.build()
}
@RestController
internal class PathController {
@GetMapping("/")
fun index():String {
return "ok"
}
}
@Bean
open fun userDetailsService(): UserDetailsService {
val userDetails = User.withDefaultPasswordEncoder()
.username("user")
.password("password")
.authorities("ROLE_USER")
.build()
val admin1Details = User.withDefaultPasswordEncoder()
.username("admin")
.password("password")
.authorities("ROLE_ADMIN")
.build()
val admin2Details = User.withDefaultPasswordEncoder()
.username("other")
.password("password")
.authorities("ROLE_OTHER")
.build()
return InMemoryUserDetailsManager(userDetails, admin1Details, admin2Details)
}
}
@Test
fun `request when secured by mvc with servlet path then responds based on servlet path`() {
this.spring.register(MvcMatcherServletPathConfig::class.java).autowire()
this.mockMvc.perform(MockMvcRequestBuilders.get("/spring/path")
.servletPath("/spring"))
.andExpect(status().isForbidden)
this.mockMvc.perform(MockMvcRequestBuilders.get("/other/path")
.servletPath("/other"))
.andExpect(status().isOk)
}
@Configuration
@EnableWebSecurity
@EnableWebMvc
open class MvcMatcherServletPathConfig {
@Bean
open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
http {
authorizeRequests {
authorize("/path",
"/spring",
denyAll)
}
}
return http.build()
}
@RestController
internal class PathController {
@RequestMapping("/path")
fun path():String {
return "ok"
}
}
}
@Configuration
@EnableWebSecurity
@EnableWebMvc
open class AuthorizeRequestsByMvcConfigWithHttpMethod{
@Bean
open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
http {
authorizeRequests {
authorize(HttpMethod.GET, "/path", permitAll)
authorize(HttpMethod.PUT, "/path", denyAll)
}
}
return http.build()
}
@RestController
internal class PathController {
@RequestMapping("/path")
fun path(): String {
return "ok"
}
}
}
@Test
fun `request when secured by mvc with http method then responds based on http method`() {
this.spring.register(AuthorizeRequestsByMvcConfigWithHttpMethod::class.java).autowire()
this.mockMvc.get("/path")
.andExpect {
status { isOk() }
}
this.mockMvc.put("/path") { with(csrf()) }
.andExpect {
status { isForbidden() }
}
}
@Configuration
@EnableWebSecurity
@EnableWebMvc
open class MvcMatcherServletPathHttpMethodConfig {
@Bean
open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
http {
authorizeRequests {
authorize(HttpMethod.GET, "/path", "/spring", denyAll)
authorize(HttpMethod.PUT, "/path", "/spring", denyAll)
}
}
return http.build()
}
@RestController
internal class PathController {
@RequestMapping("/path")
fun path(): String {
return "ok"
}
}
}
@Test
fun `request when secured by mvc with servlet path and http method then responds based on path and method`() {
this.spring.register(MvcMatcherServletPathConfig::class.java).autowire()
this.mockMvc.perform(MockMvcRequestBuilders.get("/spring/path")
.servletPath("/spring"))
.andExpect(status().isForbidden)
this.mockMvc.perform(MockMvcRequestBuilders.put("/spring/path")
.servletPath("/spring"))
.andExpect(status().isForbidden)
this.mockMvc.perform(MockMvcRequestBuilders.get("/other/path")
.servletPath("/other"))
.andExpect(status().isOk)
}
}

View File

@ -23,7 +23,6 @@
<suppress files="ELRequestMatcherContext\.java" checks="SpringMethodVisibility"/>
<suppress files="EnableWebFluxSecurityTests\.java" checks="SpringMethodVisibility"/>
<suppress files="ExpressionBasedPreInvocationAdviceTests\.java" checks="SpringMethodVisibility"/>
<suppress files="ExpressionUrlAuthorizationConfigurerTests\.java" checks="SpringMethodVisibility"/>
<suppress files="Jsr250MethodSecurityMetadataSourceTests\.java" checks="SpringMethodVisibility"/>
<suppress files="MapBasedMethodSecurityMetadataSourceTests\.java" checks="SpringMethodVisibility"/>
<suppress files="OAuth2ResourceServerBeanDefinitionParserTests\.java" checks="SpringMethodVisibility"/>