Skip to content

Commit

Permalink
Use CompositeFilterChainProxy
Browse files Browse the repository at this point in the history
By extending FilterChainProxy CompositeFilterChainProxy is more passive since
users often depend on the type of the springSecurityFilterChain Bean being
FilterChainProxy (even though it can already be other types - when debug is
enabled).

Issue gh-14128
  • Loading branch information
rwinch committed Dec 15, 2023
1 parent 70dfb3d commit 142b268
Showing 1 changed file with 78 additions and 20 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,21 @@

package org.springframework.security.config.annotation.web.configuration;

import java.io.IOException;
import java.util.List;

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.FactoryBean;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
Expand All @@ -42,6 +46,8 @@
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.access.HandlerMappingIntrospectorRequestTransformer;
import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer;
import org.springframework.security.web.firewall.HttpFirewall;
import org.springframework.security.web.firewall.RequestRejectedHandler;
import org.springframework.security.web.method.annotation.AuthenticationPrincipalArgumentResolver;
import org.springframework.security.web.method.annotation.CsrfTokenArgumentResolver;
import org.springframework.security.web.method.annotation.CurrentSecurityContextArgumentResolver;
Expand Down Expand Up @@ -135,21 +141,18 @@ public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) t
registry.registerBeanDefinition(HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME + "RequestTransformer",
hmiRequestTransformer);

String filterChainProxyBeanName = AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME
+ "Proxy";
BeanDefinition filterChainProxy = registry
.getBeanDefinition(AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME);
registry.registerBeanDefinition(filterChainProxyBeanName, filterChainProxy);

BeanDefinitionBuilder hmiCacheFilterBldr = BeanDefinitionBuilder
.rootBeanDefinition(HandlerMappingIntrospectorCachFilterFactoryBean.class)
.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);

ManagedList<BeanMetadataElement> filters = new ManagedList<>();
filters.add(hmiCacheFilterBldr.getBeanDefinition());
filters.add(new RuntimeBeanReference(filterChainProxyBeanName));
filters.add(filterChainProxy);
BeanDefinitionBuilder compositeSpringSecurityFilterChainBldr = BeanDefinitionBuilder
.rootBeanDefinition(SpringSecurityFilterCompositeFilter.class)
.rootBeanDefinition(CompositeFilterChainProxy.class)
.addConstructorArgValue(filters);

registry.removeBeanDefinition(AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME);
Expand Down Expand Up @@ -188,21 +191,73 @@ public Class<?> getObjectType() {
}

/**
* Extension to {@link CompositeFilter} to expose private methods used by Spring
* Security's test support
* Extends {@link FilterChainProxy} to provide as much passivity as possible but
* delegates to {@link CompositeFilter} for
* {@link #doFilter(ServletRequest, ServletResponse, FilterChain)}.
*/
static class SpringSecurityFilterCompositeFilter extends CompositeFilter {
static class CompositeFilterChainProxy extends FilterChainProxy {

private FilterChainProxy springSecurityFilterChain;
/**
* Used for {@link #doFilter(ServletRequest, ServletResponse, FilterChain)}
*/
private final Filter doFilterDelegate;

SpringSecurityFilterCompositeFilter(List<? extends Filter> filters) {
setFilters(filters); // for the parent
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);
}

@Override
public void setFilters(List<? extends Filter> filters) {
super.setFilters(filters);
this.springSecurityFilterChain = findFilterChainProxy(filters);
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);
}

/**
Expand All @@ -212,7 +267,7 @@ public void setFilters(List<? extends Filter> filters) {
* @return
*/
private List<? extends Filter> getFilters(HttpServletRequest request) {
List<SecurityFilterChain> filterChains = getFilterChainProxy().getFilterChains();
List<SecurityFilterChain> filterChains = this.springSecurityFilterChain.getFilterChains();
for (SecurityFilterChain chain : filterChains) {
if (chain.matches(request)) {
return chain.getFilters();
Expand All @@ -222,11 +277,14 @@ private List<? extends Filter> getFilters(HttpServletRequest request) {
}

/**
* Used by Spring Security's Test support to find the FilterChainProxy
* @return
* Creates the Filter to delegate to for doFilter
* @param filters the Filters to delegate to.
* @return the Filter for doFilter
*/
private FilterChainProxy getFilterChainProxy() {
return this.springSecurityFilterChain;
private static Filter createDoFilterDelegate(List<? extends Filter> filters) {
CompositeFilter delegate = new CompositeFilter();
delegate.setFilters(filters);
return delegate;
}

/**
Expand Down

0 comments on commit 142b268

Please sign in to comment.