diff --git a/config/src/main/java/org/springframework/security/config/annotation/web/builders/WebSecurity.java b/config/src/main/java/org/springframework/security/config/annotation/web/builders/WebSecurity.java index 43329137fcf..0974736d019 100644 --- a/config/src/main/java/org/springframework/security/config/annotation/web/builders/WebSecurity.java +++ b/config/src/main/java/org/springframework/security/config/annotation/web/builders/WebSecurity.java @@ -302,16 +302,18 @@ protected Filter performBuild() throws Exception { requestMatcherPrivilegeEvaluatorsEntries .add(getRequestMatcherPrivilegeEvaluatorsEntry(securityFilterChain)); } - boolean anyRequestConfigured = false; + DefaultSecurityFilterChain anyRequestFilterChain = null; for (SecurityBuilder securityFilterChainBuilder : this.securityFilterChainBuilders) { SecurityFilterChain securityFilterChain = securityFilterChainBuilder.build(); - Assert.isTrue(!anyRequestConfigured, - "A filter chain that matches any request has already been configured, which means that this filter chain [" - + securityFilterChain - + "] will never get invoked. Please use `HttpSecurity#securityMatcher` to ensure that there is only one filter chain configured for 'any request' and that the 'any request' filter chain is published last."); + if (anyRequestFilterChain != null) { + String message = "A filter chain that matches any request [" + anyRequestFilterChain + + "] has already been configured, which means that this filter chain [" + securityFilterChain + + "] will never get invoked. Please use `HttpSecurity#securityMatcher` to ensure that there is only one filter chain configured for 'any request' and that the 'any request' filter chain is published last."; + throw new IllegalArgumentException(message); + } if (securityFilterChain instanceof DefaultSecurityFilterChain defaultSecurityFilterChain) { if (defaultSecurityFilterChain.getRequestMatcher() instanceof AnyRequestMatcher) { - anyRequestConfigured = true; + anyRequestFilterChain = defaultSecurityFilterChain; } } securityFilterChains.add(securityFilterChain); diff --git a/web/src/main/java/org/springframework/security/web/DefaultSecurityFilterChain.java b/web/src/main/java/org/springframework/security/web/DefaultSecurityFilterChain.java index b5908be016c..e643d773506 100644 --- a/web/src/main/java/org/springframework/security/web/DefaultSecurityFilterChain.java +++ b/web/src/main/java/org/springframework/security/web/DefaultSecurityFilterChain.java @@ -24,7 +24,14 @@ import jakarta.servlet.http.HttpServletRequest; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import reactor.util.annotation.NonNull; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.BeanFactory; +import org.springframework.beans.factory.BeanFactoryAware; +import org.springframework.beans.factory.BeanNameAware; +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.core.log.LogMessage; import org.springframework.security.web.util.matcher.RequestMatcher; import org.springframework.util.StringUtils; @@ -36,7 +43,7 @@ * @author Jinwoo Bae * @since 3.1 */ -public final class DefaultSecurityFilterChain implements SecurityFilterChain { +public final class DefaultSecurityFilterChain implements SecurityFilterChain, BeanNameAware, BeanFactoryAware { private static final Log logger = LogFactory.getLog(DefaultSecurityFilterChain.class); @@ -44,6 +51,10 @@ public final class DefaultSecurityFilterChain implements SecurityFilterChain { private final List filters; + private String beanName; + + private ConfigurableListableBeanFactory beanFactory; + public DefaultSecurityFilterChain(RequestMatcher requestMatcher, Filter... filters) { this(requestMatcher, Arrays.asList(filters)); } @@ -80,8 +91,38 @@ public boolean matches(HttpServletRequest request) { @Override public String toString() { - return this.getClass().getSimpleName() + " [RequestMatcher=" + this.requestMatcher + ", Filters=" + this.filters - + "]"; + List filterNames = new ArrayList<>(); + for (Filter filter : this.filters) { + String name = filter.getClass().getSimpleName(); + if (name.endsWith("Filter")) { + name = name.substring(0, name.length() - "Filter".length()); + } + filterNames.add(name); + } + String declaration = this.getClass().getSimpleName(); + if (this.beanName != null) { + declaration += " defined as '" + this.beanName + "'"; + if (this.beanFactory != null) { + BeanDefinition bd = this.beanFactory.getBeanDefinition(this.beanName); + String description = bd.getResourceDescription(); + if (description != null) { + declaration += " in [" + description + "]"; + } + } + } + return declaration + " matching [" + this.requestMatcher + "] and having filters " + filterNames; + } + + @Override + public void setBeanName(@NonNull String name) { + this.beanName = name; + } + + @Override + public void setBeanFactory(BeanFactory beanFactory) throws BeansException { + if (beanFactory instanceof ConfigurableListableBeanFactory listable) { + this.beanFactory = listable; + } } }