14 changed files with 4 additions and 3886 deletions
@ -1,196 +0,0 @@
@@ -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 @@
@@ -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; |
||||
} |
||||
|
||||
} |
||||
|
||||
} |
||||
@ -1,333 +0,0 @@
@@ -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 @@
@@ -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) |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
@ -1,561 +0,0 @@
@@ -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
@ -1,254 +0,0 @@
@@ -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 @@
@@ -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) |
||||
} |
||||
} |
||||
Loading…
Reference in new issue