mirror of
https://github.com/spring-projects/spring-security.git
synced 2025-07-10 12:23:30 +00:00
Remove authorizeRequests
Closes gh-15174
This commit is contained in:
parent
2c87270dbc
commit
f1725b25a0
@ -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>
|
||||
* @Configuration
|
||||
* @EnableWebSecurity
|
||||
* public class AuthorizeUrlsSecurityConfig {
|
||||
*
|
||||
* @Bean
|
||||
* public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
|
||||
* http
|
||||
* .authorizeRequests((authorizeRequests) ->
|
||||
* authorizeRequests
|
||||
* .requestMatchers("/**").hasRole("USER")
|
||||
* )
|
||||
* .formLogin(withDefaults());
|
||||
* return http.build();
|
||||
* }
|
||||
*
|
||||
* @Bean
|
||||
* public UserDetailsService userDetailsService() {
|
||||
* UserDetails user = User.withDefaultPasswordEncoder()
|
||||
* .username("user")
|
||||
* .password("password")
|
||||
* .roles("USER")
|
||||
* .build();
|
||||
* UserDetails admin = User.withDefaultPasswordEncoder()
|
||||
* .username("admin")
|
||||
* .password("password")
|
||||
* .roles("ADMIN", "USER")
|
||||
* .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>
|
||||
* @Configuration
|
||||
* @EnableWebSecurity
|
||||
* public class AuthorizeUrlsSecurityConfig {
|
||||
*
|
||||
* @Bean
|
||||
* public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
|
||||
* http
|
||||
* .authorizeRequests((authorizeRequests) ->
|
||||
* authorizeRequests
|
||||
* .requestMatchers("/admin/**").hasRole("ADMIN")
|
||||
* .requestMatchers("/**").hasRole("USER")
|
||||
* )
|
||||
* .formLogin(withDefaults());
|
||||
* return http.build();
|
||||
* }
|
||||
*
|
||||
* @Bean
|
||||
* public UserDetailsService userDetailsService() {
|
||||
* UserDetails user = User.withDefaultPasswordEncoder()
|
||||
* .username("user")
|
||||
* .password("password")
|
||||
* .roles("USER")
|
||||
* .build();
|
||||
* UserDetails admin = User.withDefaultPasswordEncoder()
|
||||
* .username("admin")
|
||||
* .password("password")
|
||||
* .roles("ADMIN", "USER")
|
||||
* .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>
|
||||
* @Configuration
|
||||
* @EnableWebSecurity
|
||||
* public class AuthorizeUrlsSecurityConfig {
|
||||
*
|
||||
* @Bean
|
||||
* public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
|
||||
* http
|
||||
* .authorizeRequests((authorizeRequests) ->
|
||||
* authorizeRequests
|
||||
* .requestMatchers("/**").hasRole("USER")
|
||||
* .requestMatchers("/admin/**").hasRole("ADMIN")
|
||||
* );
|
||||
* 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) {
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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>
|
||||
* @Bean
|
||||
* public SecurityFilterChain filterChain(HttpSecurity http, ApplicationContext context) throws Exception {
|
||||
* http.apply(new UrlAuthorizationConfigurer<HttpSecurity>(context)).getRegistry()
|
||||
* .requestMatchers("/users**", "/sessions/**").hasRole("USER")
|
||||
* .requestMatchers("/signup").hasRole("ANONYMOUS").anyRequest().hasRole("USER");
|
||||
* }
|
||||
* </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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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]
|
||||
*
|
||||
|
@ -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";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -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
|
||||
|
@ -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";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
@ -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"/>
|
||||
|
Loading…
x
Reference in New Issue
Block a user