mirror of
https://github.com/spring-projects/spring-security.git
synced 2025-05-31 09:12:14 +00:00
Deprecate HandlerMappingIntrospectorRequestTransformer
Closes gh-16536
This commit is contained in:
parent
1fb3fc80f9
commit
f93a7a2f85
@ -35,7 +35,9 @@ import org.springframework.core.ResolvableType;
|
||||
import org.springframework.security.access.PermissionEvaluator;
|
||||
import org.springframework.security.access.expression.SecurityExpressionHandler;
|
||||
import org.springframework.security.access.hierarchicalroles.RoleHierarchy;
|
||||
import org.springframework.security.authorization.AuthorizationDecision;
|
||||
import org.springframework.security.authorization.AuthorizationManager;
|
||||
import org.springframework.security.authorization.SingleResultAuthorizationManager;
|
||||
import org.springframework.security.config.ObjectPostProcessor;
|
||||
import org.springframework.security.config.annotation.AbstractConfiguredSecurityBuilder;
|
||||
import org.springframework.security.config.annotation.SecurityBuilder;
|
||||
@ -58,6 +60,8 @@ import org.springframework.security.web.access.WebInvocationPrivilegeEvaluator;
|
||||
import org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler;
|
||||
import org.springframework.security.web.access.intercept.AuthorizationFilter;
|
||||
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
|
||||
import org.springframework.security.web.access.intercept.RequestAuthorizationContext;
|
||||
import org.springframework.security.web.access.intercept.RequestMatcherDelegatingAuthorizationManager;
|
||||
import org.springframework.security.web.debug.DebugFilter;
|
||||
import org.springframework.security.web.firewall.CompositeRequestRejectedHandler;
|
||||
import org.springframework.security.web.firewall.HttpFirewall;
|
||||
@ -65,6 +69,7 @@ import org.springframework.security.web.firewall.HttpStatusRequestRejectedHandle
|
||||
import org.springframework.security.web.firewall.ObservationMarkingRequestRejectedHandler;
|
||||
import org.springframework.security.web.firewall.RequestRejectedHandler;
|
||||
import org.springframework.security.web.firewall.StrictHttpFirewall;
|
||||
import org.springframework.security.web.util.matcher.AnyRequestMatcher;
|
||||
import org.springframework.security.web.util.matcher.RequestMatcher;
|
||||
import org.springframework.security.web.util.matcher.RequestMatcherEntry;
|
||||
import org.springframework.util.Assert;
|
||||
@ -230,8 +235,8 @@ public final class WebSecurity extends AbstractConfiguredSecurityBuilder<Filter,
|
||||
|
||||
/**
|
||||
* Set the {@link WebInvocationPrivilegeEvaluator} to be used. If this is not
|
||||
* specified, then a {@link RequestMatcherDelegatingWebInvocationPrivilegeEvaluator}
|
||||
* will be created based on the list of {@link SecurityFilterChain}.
|
||||
* specified, then a {@link AuthorizationManagerWebInvocationPrivilegeEvaluator} will
|
||||
* be created based on the list of {@link SecurityFilterChain}.
|
||||
* @param privilegeEvaluator the {@link WebInvocationPrivilegeEvaluator} to use
|
||||
* @return the {@link WebSecurity} for further customizations
|
||||
*/
|
||||
@ -300,24 +305,33 @@ public final class WebSecurity extends AbstractConfiguredSecurityBuilder<Filter,
|
||||
+ ".addSecurityFilterChainBuilder directly");
|
||||
int chainSize = this.ignoredRequests.size() + this.securityFilterChainBuilders.size();
|
||||
List<SecurityFilterChain> securityFilterChains = new ArrayList<>(chainSize);
|
||||
List<RequestMatcherEntry<List<WebInvocationPrivilegeEvaluator>>> requestMatcherPrivilegeEvaluatorsEntries = new ArrayList<>();
|
||||
RequestMatcherDelegatingAuthorizationManager.Builder builder = RequestMatcherDelegatingAuthorizationManager
|
||||
.builder();
|
||||
boolean mappings = false;
|
||||
for (RequestMatcher ignoredRequest : this.ignoredRequests) {
|
||||
WebSecurity.this.logger.warn("You are asking Spring Security to ignore " + ignoredRequest
|
||||
+ ". This is not recommended -- please use permitAll via HttpSecurity#authorizeHttpRequests instead.");
|
||||
SecurityFilterChain securityFilterChain = new DefaultSecurityFilterChain(ignoredRequest);
|
||||
securityFilterChains.add(securityFilterChain);
|
||||
requestMatcherPrivilegeEvaluatorsEntries
|
||||
.add(getRequestMatcherPrivilegeEvaluatorsEntry(securityFilterChain));
|
||||
builder.add(ignoredRequest, SingleResultAuthorizationManager.permitAll());
|
||||
mappings = true;
|
||||
}
|
||||
for (SecurityBuilder<? extends SecurityFilterChain> securityFilterChainBuilder : this.securityFilterChainBuilders) {
|
||||
SecurityFilterChain securityFilterChain = securityFilterChainBuilder.build();
|
||||
securityFilterChains.add(securityFilterChain);
|
||||
requestMatcherPrivilegeEvaluatorsEntries
|
||||
.add(getRequestMatcherPrivilegeEvaluatorsEntry(securityFilterChain));
|
||||
mappings = addAuthorizationManager(securityFilterChain, builder) || mappings;
|
||||
}
|
||||
if (this.privilegeEvaluator == null) {
|
||||
AuthorizationManager<HttpServletRequest> authorizationManager = mappings ? builder.build()
|
||||
: SingleResultAuthorizationManager.permitAll();
|
||||
AuthorizationManagerWebInvocationPrivilegeEvaluator privilegeEvaluator = new AuthorizationManagerWebInvocationPrivilegeEvaluator(
|
||||
authorizationManager);
|
||||
privilegeEvaluator.setServletContext(this.servletContext);
|
||||
if (this.privilegeEvaluatorRequestTransformer != null) {
|
||||
privilegeEvaluator.setRequestTransformer(this.privilegeEvaluatorRequestTransformer);
|
||||
}
|
||||
this.privilegeEvaluator = new RequestMatcherDelegatingWebInvocationPrivilegeEvaluator(
|
||||
requestMatcherPrivilegeEvaluatorsEntries);
|
||||
List.of(new RequestMatcherEntry<>(AnyRequestMatcher.INSTANCE, List.of(privilegeEvaluator))));
|
||||
}
|
||||
FilterChainProxy filterChainProxy = new FilterChainProxy(securityFilterChains);
|
||||
if (this.httpFirewall != null) {
|
||||
@ -350,30 +364,32 @@ public final class WebSecurity extends AbstractConfiguredSecurityBuilder<Filter,
|
||||
return result;
|
||||
}
|
||||
|
||||
private RequestMatcherEntry<List<WebInvocationPrivilegeEvaluator>> getRequestMatcherPrivilegeEvaluatorsEntry(
|
||||
SecurityFilterChain securityFilterChain) {
|
||||
List<WebInvocationPrivilegeEvaluator> privilegeEvaluators = new ArrayList<>();
|
||||
private boolean addAuthorizationManager(SecurityFilterChain securityFilterChain,
|
||||
RequestMatcherDelegatingAuthorizationManager.Builder builder) {
|
||||
boolean mappings = false;
|
||||
for (Filter filter : securityFilterChain.getFilters()) {
|
||||
if (filter instanceof FilterSecurityInterceptor) {
|
||||
DefaultWebInvocationPrivilegeEvaluator defaultWebInvocationPrivilegeEvaluator = new DefaultWebInvocationPrivilegeEvaluator(
|
||||
(FilterSecurityInterceptor) filter);
|
||||
defaultWebInvocationPrivilegeEvaluator.setServletContext(this.servletContext);
|
||||
privilegeEvaluators.add(defaultWebInvocationPrivilegeEvaluator);
|
||||
if (filter instanceof FilterSecurityInterceptor securityInterceptor) {
|
||||
DefaultWebInvocationPrivilegeEvaluator privilegeEvaluator = new DefaultWebInvocationPrivilegeEvaluator(
|
||||
securityInterceptor);
|
||||
privilegeEvaluator.setServletContext(this.servletContext);
|
||||
AuthorizationManager<RequestAuthorizationContext> authorizationManager = (authentication, context) -> {
|
||||
HttpServletRequest request = context.getRequest();
|
||||
boolean result = privilegeEvaluator.isAllowed(request.getContextPath(), request.getRequestURI(),
|
||||
request.getMethod(), authentication.get());
|
||||
return new AuthorizationDecision(result);
|
||||
};
|
||||
builder.add(securityFilterChain::matches, authorizationManager);
|
||||
mappings = true;
|
||||
continue;
|
||||
}
|
||||
if (filter instanceof AuthorizationFilter) {
|
||||
AuthorizationManager<HttpServletRequest> authorizationManager = ((AuthorizationFilter) filter)
|
||||
.getAuthorizationManager();
|
||||
AuthorizationManagerWebInvocationPrivilegeEvaluator evaluator = new AuthorizationManagerWebInvocationPrivilegeEvaluator(
|
||||
authorizationManager);
|
||||
evaluator.setServletContext(this.servletContext);
|
||||
if (this.privilegeEvaluatorRequestTransformer != null) {
|
||||
evaluator.setRequestTransformer(this.privilegeEvaluatorRequestTransformer);
|
||||
}
|
||||
privilegeEvaluators.add(evaluator);
|
||||
if (filter instanceof AuthorizationFilter authorization) {
|
||||
AuthorizationManager<HttpServletRequest> authorizationManager = authorization.getAuthorizationManager();
|
||||
builder.add(securityFilterChain::matches,
|
||||
(authentication, context) -> authorizationManager.check(authentication, context.getRequest()));
|
||||
mappings = true;
|
||||
}
|
||||
}
|
||||
return new RequestMatcherEntry<>(securityFilterChain::matches, privilegeEvaluators);
|
||||
return mappings;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -78,6 +78,8 @@ class WebMvcSecurityConfiguration implements WebMvcConfigurer, ApplicationContex
|
||||
|
||||
private static final String HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME = "mvcHandlerMappingIntrospector";
|
||||
|
||||
private static final String PATH_PATTERN_REQUEST_TRANSFORMER_BEAN_NAME = "pathPatternRequestTransformer";
|
||||
|
||||
private BeanResolver beanResolver;
|
||||
|
||||
private SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder
|
||||
@ -119,18 +121,8 @@ class WebMvcSecurityConfiguration implements WebMvcConfigurer, ApplicationContex
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to ensure Spring MVC request matching is cached.
|
||||
*
|
||||
* Creates a {@link BeanDefinitionRegistryPostProcessor} that detects if a bean named
|
||||
* HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME is defined. If so, it moves the
|
||||
* AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME to another bean name
|
||||
* and then adds a {@link CompositeFilter} that contains
|
||||
* {@link HandlerMappingIntrospector#createCacheFilter()} and the original
|
||||
* FilterChainProxy under the original Bean name.
|
||||
* @return
|
||||
*/
|
||||
@Bean
|
||||
@Deprecated
|
||||
static BeanDefinitionRegistryPostProcessor springSecurityHandlerMappingIntrospectorBeanDefinitionRegistryPostProcessor() {
|
||||
return new BeanDefinitionRegistryPostProcessor() {
|
||||
@Override
|
||||
@ -144,12 +136,15 @@ class WebMvcSecurityConfiguration implements WebMvcConfigurer, ApplicationContex
|
||||
}
|
||||
|
||||
String hmiRequestTransformerBeanName = HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME + "RequestTransformer";
|
||||
if (!registry.containsBeanDefinition(hmiRequestTransformerBeanName)) {
|
||||
BeanDefinition hmiRequestTransformer = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(HandlerMappingIntrospectorRequestTransformer.class)
|
||||
.addConstructorArgReference(HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME)
|
||||
.getBeanDefinition();
|
||||
registry.registerBeanDefinition(hmiRequestTransformerBeanName, hmiRequestTransformer);
|
||||
if (!registry.containsBeanDefinition(PATH_PATTERN_REQUEST_TRANSFORMER_BEAN_NAME)
|
||||
&& !registry.containsBeanDefinition(hmiRequestTransformerBeanName)) {
|
||||
if (!registry.containsBeanDefinition(hmiRequestTransformerBeanName)) {
|
||||
BeanDefinition hmiRequestTransformer = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(HandlerMappingIntrospectorRequestTransformer.class)
|
||||
.addConstructorArgReference(HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME)
|
||||
.getBeanDefinition();
|
||||
registry.registerBeanDefinition(hmiRequestTransformerBeanName, hmiRequestTransformer);
|
||||
}
|
||||
}
|
||||
|
||||
BeanDefinition filterChainProxy = registry
|
||||
@ -178,7 +173,11 @@ class WebMvcSecurityConfiguration implements WebMvcConfigurer, ApplicationContex
|
||||
/**
|
||||
* {@link FactoryBean} to defer creation of
|
||||
* {@link HandlerMappingIntrospector#createCacheFilter()}
|
||||
*
|
||||
* @deprecated see {@link WebSecurityConfiguration} for
|
||||
* {@link org.springframework.web.util.pattern.PathPattern} replacement
|
||||
*/
|
||||
@Deprecated
|
||||
static class HandlerMappingIntrospectorCacheFilterFactoryBean
|
||||
implements ApplicationContextAware, FactoryBean<Filter> {
|
||||
|
||||
@ -207,7 +206,11 @@ class WebMvcSecurityConfiguration implements WebMvcConfigurer, ApplicationContex
|
||||
* Extends {@link FilterChainProxy} to provide as much passivity as possible but
|
||||
* delegates to {@link CompositeFilter} for
|
||||
* {@link #doFilter(ServletRequest, ServletResponse, FilterChain)}.
|
||||
*
|
||||
* @deprecated see {@link WebSecurityConfiguration} for
|
||||
* {@link org.springframework.web.util.pattern.PathPattern} replacement
|
||||
*/
|
||||
@Deprecated
|
||||
static class CompositeFilterChainProxy extends FilterChainProxy {
|
||||
|
||||
/**
|
||||
|
@ -16,16 +16,29 @@
|
||||
|
||||
package org.springframework.security.config.annotation.web.configuration;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import jakarta.servlet.Filter;
|
||||
import jakarta.servlet.FilterChain;
|
||||
import jakarta.servlet.ServletException;
|
||||
import jakarta.servlet.ServletRequest;
|
||||
import jakarta.servlet.ServletResponse;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.springframework.beans.BeanMetadataElement;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
|
||||
import org.springframework.beans.factory.support.ManagedList;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.DependsOn;
|
||||
@ -45,11 +58,18 @@ import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.builders.WebSecurity;
|
||||
import org.springframework.security.config.crypto.RsaKeyConversionServicePostProcessor;
|
||||
import org.springframework.security.context.DelegatingApplicationListener;
|
||||
import org.springframework.security.core.context.SecurityContextHolderStrategy;
|
||||
import org.springframework.security.web.FilterChainProxy;
|
||||
import org.springframework.security.web.FilterInvocation;
|
||||
import org.springframework.security.web.SecurityFilterChain;
|
||||
import org.springframework.security.web.access.WebInvocationPrivilegeEvaluator;
|
||||
import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer;
|
||||
import org.springframework.security.web.debug.DebugFilter;
|
||||
import org.springframework.security.web.firewall.HttpFirewall;
|
||||
import org.springframework.security.web.firewall.RequestRejectedHandler;
|
||||
import org.springframework.web.filter.CompositeFilter;
|
||||
import org.springframework.web.filter.ServletRequestPathFilter;
|
||||
import org.springframework.web.servlet.handler.HandlerMappingIntrospector;
|
||||
|
||||
/**
|
||||
* Uses a {@link WebSecurity} to create the {@link FilterChainProxy} that performs the web
|
||||
@ -186,6 +206,56 @@ public class WebSecurityConfiguration implements ImportAware {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to ensure Spring MVC request matching is cached.
|
||||
*
|
||||
* Creates a {@link BeanDefinitionRegistryPostProcessor} that detects if a bean named
|
||||
* HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME is defined. If so, it moves the
|
||||
* AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME to another bean name
|
||||
* and then adds a {@link CompositeFilter} that contains
|
||||
* {@link HandlerMappingIntrospector#createCacheFilter()} and the original
|
||||
* FilterChainProxy under the original Bean name.
|
||||
* @return
|
||||
*/
|
||||
@Bean
|
||||
static BeanDefinitionRegistryPostProcessor springSecurityPathPatternParserBeanDefinitionRegistryPostProcessor() {
|
||||
return new BeanDefinitionRegistryPostProcessor() {
|
||||
@Override
|
||||
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
|
||||
BeanDefinition filterChainProxy = registry
|
||||
.getBeanDefinition(AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME);
|
||||
|
||||
if (filterChainProxy.getResolvableType().isAssignableFrom(CompositeFilterChainProxy.class)) {
|
||||
return;
|
||||
}
|
||||
|
||||
BeanDefinitionBuilder pppCacheFilterBldr = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(ServletRequestPathFilter.class)
|
||||
.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||
|
||||
ManagedList<BeanMetadataElement> filters = new ManagedList<>();
|
||||
filters.add(pppCacheFilterBldr.getBeanDefinition());
|
||||
filters.add(filterChainProxy);
|
||||
BeanDefinitionBuilder compositeSpringSecurityFilterChainBldr = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(CompositeFilterChainProxy.class)
|
||||
.addConstructorArgValue(filters);
|
||||
|
||||
if (filterChainProxy.getResolvableType().isInstance(DebugFilter.class)) {
|
||||
compositeSpringSecurityFilterChainBldr = BeanDefinitionBuilder.rootBeanDefinition(DebugFilter.class)
|
||||
.addConstructorArgValue(compositeSpringSecurityFilterChainBldr.getBeanDefinition());
|
||||
}
|
||||
|
||||
registry.removeBeanDefinition(AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME);
|
||||
registry.registerBeanDefinition(AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME,
|
||||
compositeSpringSecurityFilterChainBldr.getBeanDefinition());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* A custom version of the Spring provided AnnotationAwareOrderComparator that uses
|
||||
* {@link AnnotationUtils#findAnnotation(Class, Class)} to look on super class
|
||||
@ -219,4 +289,126 @@ public class WebSecurityConfiguration implements ImportAware {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Extends {@link FilterChainProxy} to provide as much passivity as possible but
|
||||
* delegates to {@link CompositeFilter} for
|
||||
* {@link #doFilter(ServletRequest, ServletResponse, FilterChain)}.
|
||||
*/
|
||||
static class CompositeFilterChainProxy extends FilterChainProxy {
|
||||
|
||||
/**
|
||||
* Used for {@link #doFilter(ServletRequest, ServletResponse, FilterChain)}
|
||||
*/
|
||||
private final Filter doFilterDelegate;
|
||||
|
||||
private final FilterChainProxy springSecurityFilterChain;
|
||||
|
||||
/**
|
||||
* Creates a new instance
|
||||
* @param filters the Filters to delegate to. One of which must be
|
||||
* FilterChainProxy.
|
||||
*/
|
||||
CompositeFilterChainProxy(List<? extends Filter> filters) {
|
||||
this.doFilterDelegate = createDoFilterDelegate(filters);
|
||||
this.springSecurityFilterChain = findFilterChainProxy(filters);
|
||||
}
|
||||
|
||||
CompositeFilterChainProxy(Filter delegate, FilterChainProxy filterChain) {
|
||||
this.doFilterDelegate = delegate;
|
||||
this.springSecurityFilterChain = filterChain;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() {
|
||||
this.springSecurityFilterChain.afterPropertiesSet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
|
||||
throws IOException, ServletException {
|
||||
this.doFilterDelegate.doFilter(request, response, chain);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Filter> getFilters(String url) {
|
||||
return this.springSecurityFilterChain.getFilters(url);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<SecurityFilterChain> getFilterChains() {
|
||||
return this.springSecurityFilterChain.getFilterChains();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSecurityContextHolderStrategy(SecurityContextHolderStrategy securityContextHolderStrategy) {
|
||||
this.springSecurityFilterChain.setSecurityContextHolderStrategy(securityContextHolderStrategy);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFilterChainValidator(FilterChainValidator filterChainValidator) {
|
||||
this.springSecurityFilterChain.setFilterChainValidator(filterChainValidator);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFilterChainDecorator(FilterChainDecorator filterChainDecorator) {
|
||||
this.springSecurityFilterChain.setFilterChainDecorator(filterChainDecorator);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFirewall(HttpFirewall firewall) {
|
||||
this.springSecurityFilterChain.setFirewall(firewall);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRequestRejectedHandler(RequestRejectedHandler requestRejectedHandler) {
|
||||
this.springSecurityFilterChain.setRequestRejectedHandler(requestRejectedHandler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Used through reflection by Spring Security's Test support to lookup the
|
||||
* FilterChainProxy Filters for a specific HttpServletRequest.
|
||||
* @param request
|
||||
* @return
|
||||
*/
|
||||
private List<? extends Filter> getFilters(HttpServletRequest request) {
|
||||
List<SecurityFilterChain> filterChains = this.springSecurityFilterChain.getFilterChains();
|
||||
for (SecurityFilterChain chain : filterChains) {
|
||||
if (chain.matches(request)) {
|
||||
return chain.getFilters();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the Filter to delegate to for doFilter
|
||||
* @param filters the Filters to delegate to.
|
||||
* @return the Filter for doFilter
|
||||
*/
|
||||
private static Filter createDoFilterDelegate(List<? extends Filter> filters) {
|
||||
CompositeFilter delegate = new CompositeFilter();
|
||||
delegate.setFilters(filters);
|
||||
return delegate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the FilterChainProxy in a List of Filter
|
||||
* @param filters
|
||||
* @return non-null FilterChainProxy
|
||||
* @throws IllegalStateException if the FilterChainProxy cannot be found
|
||||
*/
|
||||
private static FilterChainProxy findFilterChainProxy(List<? extends Filter> filters) {
|
||||
for (Filter filter : filters) {
|
||||
if (filter instanceof FilterChainProxy fcp) {
|
||||
return fcp;
|
||||
}
|
||||
if (filter instanceof DebugFilter debugFilter) {
|
||||
return new CompositeFilterChainProxy(debugFilter, debugFilter.getFilterChainProxy());
|
||||
}
|
||||
}
|
||||
throw new IllegalStateException("Couldn't find FilterChainProxy in " + filters);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright 2002-2023 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.aot.hint;
|
||||
|
||||
import org.springframework.aot.hint.MemberCategory;
|
||||
import org.springframework.aot.hint.RuntimeHints;
|
||||
import org.springframework.aot.hint.RuntimeHintsRegistrar;
|
||||
import org.springframework.aot.hint.TypeReference;
|
||||
|
||||
/**
|
||||
* Runtime hints for
|
||||
* {@link org.springframework.security.config.annotation.web.configuration.WebMvcSecurityConfiguration}
|
||||
*
|
||||
* @author Marcus da Coregio
|
||||
*/
|
||||
class WebSecurityConfigurationRuntimeHints implements RuntimeHintsRegistrar {
|
||||
|
||||
@Override
|
||||
public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
|
||||
hints.reflection()
|
||||
.registerType(TypeReference
|
||||
.of("org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration$CompositeFilterChainProxy"),
|
||||
MemberCategory.INVOKE_DECLARED_CONSTRUCTORS);
|
||||
}
|
||||
|
||||
}
|
@ -51,6 +51,7 @@ import org.springframework.security.core.session.SessionRegistryImpl;
|
||||
import org.springframework.security.web.access.AuthorizationManagerWebInvocationPrivilegeEvaluator;
|
||||
import org.springframework.security.web.access.DefaultWebInvocationPrivilegeEvaluator;
|
||||
import org.springframework.security.web.access.HandlerMappingIntrospectorRequestTransformer;
|
||||
import org.springframework.security.web.access.PathPatternRequestTransformer;
|
||||
import org.springframework.security.web.access.channel.ChannelDecisionManagerImpl;
|
||||
import org.springframework.security.web.access.channel.ChannelProcessingFilter;
|
||||
import org.springframework.security.web.access.channel.InsecureChannelProcessor;
|
||||
@ -974,10 +975,17 @@ class HttpConfigurationBuilder {
|
||||
@Override
|
||||
public AuthorizationManagerWebInvocationPrivilegeEvaluator.HttpServletRequestTransformer getObject()
|
||||
throws Exception {
|
||||
AuthorizationManagerWebInvocationPrivilegeEvaluator.HttpServletRequestTransformer requestTransformer = this.applicationContext
|
||||
.getBeanProvider(
|
||||
AuthorizationManagerWebInvocationPrivilegeEvaluator.HttpServletRequestTransformer.class)
|
||||
.getIfUnique();
|
||||
if (requestTransformer != null) {
|
||||
return requestTransformer;
|
||||
}
|
||||
HandlerMappingIntrospector hmi = this.applicationContext.getBeanProvider(HandlerMappingIntrospector.class)
|
||||
.getIfAvailable();
|
||||
return (hmi != null) ? new HandlerMappingIntrospectorRequestTransformer(hmi)
|
||||
: AuthorizationManagerWebInvocationPrivilegeEvaluator.HttpServletRequestTransformer.IDENTITY;
|
||||
: new PathPatternRequestTransformer();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -57,9 +57,12 @@ import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.web.FilterChainProxy;
|
||||
import org.springframework.security.web.FilterInvocation;
|
||||
import org.springframework.security.web.SecurityFilterChain;
|
||||
import org.springframework.security.web.access.AuthorizationManagerWebInvocationPrivilegeEvaluator;
|
||||
import org.springframework.security.web.access.PathPatternRequestTransformer;
|
||||
import org.springframework.security.web.access.RequestMatcherDelegatingWebInvocationPrivilegeEvaluator;
|
||||
import org.springframework.security.web.access.WebInvocationPrivilegeEvaluator;
|
||||
import org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler;
|
||||
import org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;
|
||||
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import org.springframework.util.ClassUtils;
|
||||
@ -69,8 +72,12 @@ import org.springframework.web.servlet.config.annotation.EnableWebMvc;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.atLeastOnce;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
@ -320,6 +327,27 @@ public class WebSecurityConfigurationTests {
|
||||
assertThat(privilegeEvaluator.isAllowed("/ignoring1/child", null)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loadConfigWhenUsePathPatternThenEvaluates() {
|
||||
this.spring.register(UsePathPatternConfig.class).autowire();
|
||||
WebInvocationPrivilegeEvaluator privilegeEvaluator = this.spring.getContext()
|
||||
.getBean(WebInvocationPrivilegeEvaluator.class);
|
||||
assertUserPermissions(privilegeEvaluator);
|
||||
assertAdminPermissions(privilegeEvaluator);
|
||||
assertAnotherUserPermission(privilegeEvaluator);
|
||||
// null authentication
|
||||
assertThat(privilegeEvaluator.isAllowed("/user", null)).isFalse();
|
||||
assertThat(privilegeEvaluator.isAllowed("/admin", null)).isFalse();
|
||||
assertThat(privilegeEvaluator.isAllowed("/another", null)).isTrue();
|
||||
assertThat(privilegeEvaluator.isAllowed("/ignoring1", null)).isTrue();
|
||||
assertThat(privilegeEvaluator.isAllowed("/ignoring1/child", null)).isTrue();
|
||||
AuthorizationManagerWebInvocationPrivilegeEvaluator.HttpServletRequestTransformer requestTransformer = this.spring
|
||||
.getContext()
|
||||
.getBean(AuthorizationManagerWebInvocationPrivilegeEvaluator.HttpServletRequestTransformer.class);
|
||||
verify(requestTransformer, atLeastOnce()).transform(any());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loadConfigWhenTwoSecurityFilterChainsPresentAndSecondWithAnyRequestThenException() {
|
||||
assertThatExceptionOfType(BeanCreationException.class)
|
||||
@ -862,6 +890,53 @@ public class WebSecurityConfigurationTests {
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
@EnableWebMvc
|
||||
@Import(AuthenticationTestConfiguration.class)
|
||||
static class UsePathPatternConfig {
|
||||
|
||||
@Bean
|
||||
AuthorizationManagerWebInvocationPrivilegeEvaluator.HttpServletRequestTransformer pathPatternRequestTransformer() {
|
||||
return spy(new PathPatternRequestTransformer());
|
||||
}
|
||||
|
||||
@Bean
|
||||
public WebSecurityCustomizer webSecurityCustomizer() {
|
||||
return (web) -> web.ignoring().requestMatchers("/ignoring1/**");
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Order(Ordered.HIGHEST_PRECEDENCE)
|
||||
public SecurityFilterChain notAuthorized(HttpSecurity http) throws Exception {
|
||||
// @formatter:off
|
||||
http
|
||||
.securityMatchers((requests) -> requests.requestMatchers(PathPatternRequestMatcher.withDefaults().matcher("/user")))
|
||||
.authorizeHttpRequests((requests) -> requests.anyRequest().hasRole("USER"));
|
||||
// @formatter:on
|
||||
return http.build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Order(Ordered.HIGHEST_PRECEDENCE + 1)
|
||||
public SecurityFilterChain admin(HttpSecurity http) throws Exception {
|
||||
// @formatter:off
|
||||
http
|
||||
.securityMatchers((requests) -> requests.requestMatchers(PathPatternRequestMatcher.withDefaults().matcher("/admin")))
|
||||
.authorizeHttpRequests((requests) -> requests.anyRequest().hasRole("ADMIN"));
|
||||
// @formatter:on
|
||||
return http.build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Order(Ordered.LOWEST_PRECEDENCE)
|
||||
public SecurityFilterChain permitAll(HttpSecurity http) throws Exception {
|
||||
http.authorizeHttpRequests((requests) -> requests.anyRequest().permitAll());
|
||||
return http.build();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
@EnableWebMvc
|
||||
|
@ -339,6 +339,54 @@ casAuthentication.setProxyReceptorUrl(PathPatternRequestMatcher.withDefaults().m
|
||||
----
|
||||
======
|
||||
|
||||
=== Migrate your WebInvocationPrivilegeEvaluator
|
||||
|
||||
If you are using Spring Security's JSP Taglibs or are using `WebInvocationPrivilegeEvaluator` directly, be aware of the following changes:
|
||||
|
||||
1. `RequestMatcherWebInvocationPrivilegeEvaluator` is deprecated in favor of `AuthorizationManagerWebInvocationPrivilegeEvaluator`
|
||||
2. `HandlerMappingIntrospectorRequestTransformer` is deprecated in favor of `PathPatternRequestTransformer`
|
||||
|
||||
If you are not constructing these directly, you can opt-in to both changes in advance by publishing a `PathPatternRequestTransformer` like so:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,role="primary"]
|
||||
----
|
||||
@Bean
|
||||
HttpServletRequestTransformer pathPatternRequestTransformer() {
|
||||
return new PathPatternRequestTransformer();
|
||||
}
|
||||
----
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,role="secondary"]
|
||||
----
|
||||
@Bean
|
||||
fun pathPatternRequestTransformer(): HttpServletRequestTransformer {
|
||||
return PathPatternRequestTransformer()
|
||||
}
|
||||
----
|
||||
|
||||
Xml::
|
||||
+
|
||||
[source,xml,role="secondary"]
|
||||
----
|
||||
<b:bean class="org.springframework.security.web.access.PathPatternRequestTransformer"/>
|
||||
----
|
||||
======
|
||||
|
||||
Spring Security will take this as a signal to use the new implementations.
|
||||
|
||||
[[NOTE]]
|
||||
----
|
||||
One difference you may notice is that `AuthorizationManagerWebPrivilegeInvocationEvaluator` allows the authentication to be `null` if the authorization rule is `permitAll`.
|
||||
|
||||
Test your endpoints that `permitAll` in case JSP requests using this same require should not, in fact, be permitted.
|
||||
----
|
||||
|
||||
== Include the Servlet Path Prefix in Authorization Rules
|
||||
|
||||
For many applications <<use-path-pattern, the above>> will make no difference since most commonly all URIs listed are matched by the default servlet.
|
||||
|
@ -46,7 +46,6 @@ import org.springframework.security.web.util.matcher.RequestMatcher;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.filter.DelegatingFilterProxy;
|
||||
import org.springframework.web.filter.GenericFilterBean;
|
||||
import org.springframework.web.filter.ServletRequestPathFilter;
|
||||
|
||||
/**
|
||||
* Delegates {@code Filter} requests to a list of Spring-managed filter beans. As of
|
||||
@ -163,8 +162,6 @@ public class FilterChainProxy extends GenericFilterBean {
|
||||
|
||||
private FilterChainDecorator filterChainDecorator = new VirtualFilterChainDecorator();
|
||||
|
||||
private Filter springWebFilter = new ServletRequestPathFilter();
|
||||
|
||||
public FilterChainProxy() {
|
||||
}
|
||||
|
||||
@ -213,29 +210,27 @@ public class FilterChainProxy extends GenericFilterBean {
|
||||
throws IOException, ServletException {
|
||||
FirewalledRequest firewallRequest = this.firewall.getFirewalledRequest((HttpServletRequest) request);
|
||||
HttpServletResponse firewallResponse = this.firewall.getFirewalledResponse((HttpServletResponse) response);
|
||||
this.springWebFilter.doFilter(firewallRequest, firewallResponse, (r, s) -> {
|
||||
List<Filter> filters = getFilters(firewallRequest);
|
||||
if (filters == null || filters.isEmpty()) {
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace(LogMessage.of(() -> "No security for " + requestLine(firewallRequest)));
|
||||
}
|
||||
firewallRequest.reset();
|
||||
this.filterChainDecorator.decorate(chain).doFilter(firewallRequest, firewallResponse);
|
||||
return;
|
||||
List<Filter> filters = getFilters(firewallRequest);
|
||||
if (filters == null || filters.isEmpty()) {
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace(LogMessage.of(() -> "No security for " + requestLine(firewallRequest)));
|
||||
}
|
||||
firewallRequest.reset();
|
||||
this.filterChainDecorator.decorate(chain).doFilter(firewallRequest, firewallResponse);
|
||||
return;
|
||||
}
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug(LogMessage.of(() -> "Securing " + requestLine(firewallRequest)));
|
||||
}
|
||||
FilterChain reset = (req, res) -> {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug(LogMessage.of(() -> "Securing " + requestLine(firewallRequest)));
|
||||
logger.debug(LogMessage.of(() -> "Secured " + requestLine(firewallRequest)));
|
||||
}
|
||||
FilterChain reset = (req, res) -> {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug(LogMessage.of(() -> "Secured " + requestLine(firewallRequest)));
|
||||
}
|
||||
// Deactivate path stripping as we exit the security filter chain
|
||||
firewallRequest.reset();
|
||||
chain.doFilter(req, res);
|
||||
};
|
||||
this.filterChainDecorator.decorate(reset, filters).doFilter(firewallRequest, firewallResponse);
|
||||
});
|
||||
// Deactivate path stripping as we exit the security filter chain
|
||||
firewallRequest.reset();
|
||||
chain.doFilter(req, res);
|
||||
};
|
||||
this.filterChainDecorator.decorate(reset, filters).doFilter(firewallRequest, firewallResponse);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -35,7 +35,9 @@ import org.springframework.web.servlet.handler.HandlerMappingIntrospector;
|
||||
* default throw {@link UnsupportedOperationException}.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @deprecated please use {@link PathPatternRequestTransformer} instead
|
||||
*/
|
||||
@Deprecated(forRemoval = true)
|
||||
public class HandlerMappingIntrospectorRequestTransformer
|
||||
implements AuthorizationManagerWebInvocationPrivilegeEvaluator.HttpServletRequestTransformer {
|
||||
|
||||
|
@ -0,0 +1,70 @@
|
||||
/*
|
||||
* 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.web.access;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletRequestWrapper;
|
||||
|
||||
import org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;
|
||||
import org.springframework.web.util.ServletRequestPathUtils;
|
||||
|
||||
/**
|
||||
* Prepares the privilege evaluator's request for {@link PathPatternRequestMatcher}
|
||||
* authorization rules.
|
||||
*
|
||||
* @author Josh Cummings
|
||||
* @since 6.5
|
||||
*/
|
||||
public final class PathPatternRequestTransformer
|
||||
implements AuthorizationManagerWebInvocationPrivilegeEvaluator.HttpServletRequestTransformer {
|
||||
|
||||
@Override
|
||||
public HttpServletRequest transform(HttpServletRequest request) {
|
||||
HttpServletRequest wrapped = new AttributesSupportingHttpServletRequest(request);
|
||||
ServletRequestPathUtils.parseAndCache(wrapped);
|
||||
return wrapped;
|
||||
}
|
||||
|
||||
private static final class AttributesSupportingHttpServletRequest extends HttpServletRequestWrapper {
|
||||
|
||||
private final Map<String, Object> attributes = new HashMap<>();
|
||||
|
||||
AttributesSupportingHttpServletRequest(HttpServletRequest request) {
|
||||
super(request);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getAttribute(String name) {
|
||||
return this.attributes.get(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAttribute(String name, Object value) {
|
||||
this.attributes.put(name, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeAttribute(String name) {
|
||||
this.attributes.remove(name);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -17,20 +17,17 @@
|
||||
package org.springframework.security.web.access;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import jakarta.servlet.ServletContext;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletRequestWrapper;
|
||||
|
||||
import org.springframework.security.authorization.AuthorizationManager;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.web.FilterInvocation;
|
||||
import org.springframework.security.web.util.matcher.RequestMatcherEntry;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.context.ServletContextAware;
|
||||
import org.springframework.web.util.ServletRequestPathUtils;
|
||||
|
||||
/**
|
||||
* A {@link WebInvocationPrivilegeEvaluator} which delegates to a list of
|
||||
@ -39,7 +36,11 @@ import org.springframework.web.util.ServletRequestPathUtils;
|
||||
*
|
||||
* @author Marcus Da Coregio
|
||||
* @since 5.5.5
|
||||
* @deprecated please use {@link AuthorizationManagerWebInvocationPrivilegeEvaluator} and
|
||||
* adapt any delegate {@link WebInvocationPrivilegeEvaluator}s into
|
||||
* {@link AuthorizationManager}s
|
||||
*/
|
||||
@Deprecated
|
||||
public final class RequestMatcherDelegatingWebInvocationPrivilegeEvaluator
|
||||
implements WebInvocationPrivilegeEvaluator, ServletContextAware {
|
||||
|
||||
@ -120,8 +121,7 @@ public final class RequestMatcherDelegatingWebInvocationPrivilegeEvaluator
|
||||
|
||||
private List<WebInvocationPrivilegeEvaluator> getDelegate(String contextPath, String uri, String method) {
|
||||
FilterInvocation filterInvocation = new FilterInvocation(contextPath, uri, method, this.servletContext);
|
||||
HttpServletRequest request = new AttributesSupportingHttpServletRequest(filterInvocation.getHttpRequest());
|
||||
ServletRequestPathUtils.parseAndCache(request);
|
||||
HttpServletRequest request = filterInvocation.getHttpRequest();
|
||||
for (RequestMatcherEntry<List<WebInvocationPrivilegeEvaluator>> delegate : this.delegates) {
|
||||
if (delegate.getRequestMatcher().matches(request)) {
|
||||
return delegate.getEntry();
|
||||
@ -135,29 +135,4 @@ public final class RequestMatcherDelegatingWebInvocationPrivilegeEvaluator
|
||||
this.servletContext = servletContext;
|
||||
}
|
||||
|
||||
private static final class AttributesSupportingHttpServletRequest extends HttpServletRequestWrapper {
|
||||
|
||||
private final Map<String, Object> attributes = new HashMap<>();
|
||||
|
||||
AttributesSupportingHttpServletRequest(HttpServletRequest request) {
|
||||
super(request);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getAttribute(String name) {
|
||||
return this.attributes.get(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAttribute(String name, Object value) {
|
||||
this.attributes.put(name, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeAttribute(String name) {
|
||||
this.attributes.remove(name);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -31,6 +31,8 @@ import org.springframework.security.authentication.TestAuthentication;
|
||||
import org.springframework.security.authorization.AuthorizationDecision;
|
||||
import org.springframework.security.authorization.AuthorizationManager;
|
||||
import org.springframework.security.web.access.AuthorizationManagerWebInvocationPrivilegeEvaluator.HttpServletRequestTransformer;
|
||||
import org.springframework.security.web.access.intercept.RequestMatcherDelegatingAuthorizationManager;
|
||||
import org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||
@ -111,4 +113,19 @@ class AuthorizationManagerWebInvocationPrivilegeEvaluatorTests {
|
||||
verify(this.authorizationManager).check(any(), eq(request));
|
||||
}
|
||||
|
||||
// gh-16771
|
||||
@Test
|
||||
void isAllowedWhenInvokesDelegateThenCachesRequestPath() {
|
||||
RequestMatcherDelegatingAuthorizationManager authorizationManager = RequestMatcherDelegatingAuthorizationManager
|
||||
.builder()
|
||||
.add(PathPatternRequestMatcher.withDefaults().matcher("/test/**"),
|
||||
(authentication, context) -> this.authorizationManager.check(authentication, context.getRequest()))
|
||||
.build();
|
||||
AuthorizationManagerWebInvocationPrivilegeEvaluator privilegeEvaluator = new AuthorizationManagerWebInvocationPrivilegeEvaluator(
|
||||
authorizationManager);
|
||||
privilegeEvaluator.setRequestTransformer(new PathPatternRequestTransformer());
|
||||
privilegeEvaluator.isAllowed("/test", TestAuthentication.authenticatedUser());
|
||||
verify(this.authorizationManager).check(any(), any());
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -22,16 +22,12 @@ import jakarta.servlet.http.HttpServletRequest;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.MockedStatic;
|
||||
import org.mockito.Mockito;
|
||||
|
||||
import org.springframework.mock.web.MockServletContext;
|
||||
import org.springframework.security.authentication.TestingAuthenticationToken;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;
|
||||
import org.springframework.security.web.util.matcher.RequestMatcher;
|
||||
import org.springframework.security.web.util.matcher.RequestMatcherEntry;
|
||||
import org.springframework.web.util.ServletRequestPathUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||
@ -178,19 +174,6 @@ class RequestMatcherDelegatingWebInvocationPrivilegeEvaluatorTests {
|
||||
.withMessageContaining("requestMatcher cannot be null");
|
||||
}
|
||||
|
||||
// gh-16771
|
||||
@Test
|
||||
void isAllowedWhenInvokesDelegateThenCachesRequestPath() {
|
||||
PathPatternRequestMatcher path = PathPatternRequestMatcher.withDefaults().matcher("/path/**");
|
||||
PathPatternRequestMatcher any = PathPatternRequestMatcher.withDefaults().matcher("/**");
|
||||
WebInvocationPrivilegeEvaluator delegating = evaluator(deny(path), deny(any));
|
||||
try (MockedStatic<ServletRequestPathUtils> utils = Mockito.mockStatic(ServletRequestPathUtils.class,
|
||||
Mockito.CALLS_REAL_METHODS)) {
|
||||
delegating.isAllowed("/uri", null);
|
||||
utils.verify(() -> ServletRequestPathUtils.parseAndCache(any()), times(1));
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
private RequestMatcherDelegatingWebInvocationPrivilegeEvaluator evaluator(RequestMatcherEntry... entries) {
|
||||
return new RequestMatcherDelegatingWebInvocationPrivilegeEvaluator(List.of(entries));
|
||||
|
Loading…
x
Reference in New Issue
Block a user