mirror of
https://github.com/spring-projects/spring-security.git
synced 2025-07-11 21:03:31 +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.CorsConfigurer;
|
||||||
import org.springframework.security.config.annotation.web.configurers.CsrfConfigurer;
|
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.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.FormLoginConfigurer;
|
||||||
import org.springframework.security.config.annotation.web.configurers.HeadersConfigurer;
|
import org.springframework.security.config.annotation.web.configurers.HeadersConfigurer;
|
||||||
import org.springframework.security.config.annotation.web.configurers.HttpBasicConfigurer;
|
import org.springframework.security.config.annotation.web.configurers.HttpBasicConfigurer;
|
||||||
@ -613,125 +612,6 @@ public final class HttpSecurity extends AbstractConfiguredSecurityBuilder<Defaul
|
|||||||
return HttpSecurity.this;
|
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
|
* Allows restricting access based upon the {@link HttpServletRequest} using
|
||||||
* {@link RequestMatcher} implementations (i.e. via URL patterns).
|
* {@link RequestMatcher} implementations (i.e. via URL patterns).
|
||||||
@ -1936,12 +1816,6 @@ public final class HttpSecurity extends AbstractConfiguredSecurityBuilder<Defaul
|
|||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
@Override
|
@Override
|
||||||
protected DefaultSecurityFilterChain performBuild() {
|
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);
|
this.filters.sort(OrderComparator.INSTANCE);
|
||||||
List<Filter> sortedFilters = new ArrayList<>(this.filters.size());
|
List<Filter> sortedFilters = new ArrayList<>(this.filters.size());
|
||||||
for (Filter filter : this.filters) {
|
for (Filter filter : this.filters) {
|
||||||
|
@ -36,8 +36,6 @@ import org.springframework.util.Assert;
|
|||||||
* @author Rob Winch
|
* @author Rob Winch
|
||||||
* @since 3.2
|
* @since 3.2
|
||||||
* @see ChannelSecurityConfigurer
|
* @see ChannelSecurityConfigurer
|
||||||
* @see UrlAuthorizationConfigurer
|
|
||||||
* @see ExpressionUrlAuthorizationConfigurer
|
|
||||||
* @deprecated In modern Spring Security APIs, each API manages its own configuration
|
* @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
|
* context. As such there is no direct replacement for this interface. In the case of
|
||||||
* method security, please see {@link SecurityAnnotationScanner} and
|
* 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 jakarta.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
import org.springframework.security.access.SecurityConfig;
|
|
||||||
import org.springframework.security.authorization.SingleResultAuthorizationManager;
|
import org.springframework.security.authorization.SingleResultAuthorizationManager;
|
||||||
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
|
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.security.web.util.matcher.RequestMatcher;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
@ -47,28 +45,17 @@ final class PermitAllSupport {
|
|||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
static void permitAll(HttpSecurityBuilder<? extends HttpSecurityBuilder<?>> http,
|
static void permitAll(HttpSecurityBuilder<? extends HttpSecurityBuilder<?>> http,
|
||||||
RequestMatcher... requestMatchers) {
|
RequestMatcher... requestMatchers) {
|
||||||
ExpressionUrlAuthorizationConfigurer<?> configurer = http
|
|
||||||
.getConfigurer(ExpressionUrlAuthorizationConfigurer.class);
|
|
||||||
AuthorizeHttpRequestsConfigurer<?> httpConfigurer = http.getConfigurer(AuthorizeHttpRequestsConfigurer.class);
|
AuthorizeHttpRequestsConfigurer<?> httpConfigurer = http.getConfigurer(AuthorizeHttpRequestsConfigurer.class);
|
||||||
|
|
||||||
boolean oneConfigurerPresent = configurer == null ^ httpConfigurer == null;
|
Assert.state(httpConfigurer != null,
|
||||||
Assert.state(oneConfigurerPresent,
|
"permitAll only works with HttpSecurity.authorizeHttpRequests(). Please define one.");
|
||||||
"permitAll only works with either HttpSecurity.authorizeRequests() or HttpSecurity.authorizeHttpRequests(). "
|
|
||||||
+ "Please define one or the other but not both.");
|
|
||||||
|
|
||||||
for (RequestMatcher matcher : requestMatchers) {
|
for (RequestMatcher matcher : requestMatchers) {
|
||||||
if (matcher != null) {
|
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());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private static final class ExactUrlRequestMatcher implements RequestMatcher {
|
private static final class ExactUrlRequestMatcher implements RequestMatcher {
|
||||||
|
|
||||||
|
@ -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)
|
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]
|
* 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)
|
assertThatExceptionOfType(BeanCreationException.class)
|
||||||
.isThrownBy(() -> this.spring.register(NoAuthorizedUrlsConfig.class).autowire())
|
.isThrownBy(() -> this.spring.register(NoAuthorizedUrlsConfig.class).autowire())
|
||||||
.withMessageContaining(
|
.withMessageContaining(
|
||||||
"permitAll only works with either HttpSecurity.authorizeRequests() or HttpSecurity.authorizeHttpRequests()");
|
"permitAll only works with HttpSecurity.authorizeHttpRequests(). Please define one.");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Configuration
|
@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="ELRequestMatcherContext\.java" checks="SpringMethodVisibility"/>
|
||||||
<suppress files="EnableWebFluxSecurityTests\.java" checks="SpringMethodVisibility"/>
|
<suppress files="EnableWebFluxSecurityTests\.java" checks="SpringMethodVisibility"/>
|
||||||
<suppress files="ExpressionBasedPreInvocationAdviceTests\.java" checks="SpringMethodVisibility"/>
|
<suppress files="ExpressionBasedPreInvocationAdviceTests\.java" checks="SpringMethodVisibility"/>
|
||||||
<suppress files="ExpressionUrlAuthorizationConfigurerTests\.java" checks="SpringMethodVisibility"/>
|
|
||||||
<suppress files="Jsr250MethodSecurityMetadataSourceTests\.java" checks="SpringMethodVisibility"/>
|
<suppress files="Jsr250MethodSecurityMetadataSourceTests\.java" checks="SpringMethodVisibility"/>
|
||||||
<suppress files="MapBasedMethodSecurityMetadataSourceTests\.java" checks="SpringMethodVisibility"/>
|
<suppress files="MapBasedMethodSecurityMetadataSourceTests\.java" checks="SpringMethodVisibility"/>
|
||||||
<suppress files="OAuth2ResourceServerBeanDefinitionParserTests\.java" checks="SpringMethodVisibility"/>
|
<suppress files="OAuth2ResourceServerBeanDefinitionParserTests\.java" checks="SpringMethodVisibility"/>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user