Skip to content

Commit ef03fac

Browse files
committed
Improve Error Message for Conflicting Filter Chains
Closes gh-15874
1 parent 8a6e129 commit ef03fac

File tree

2 files changed

+52
-9
lines changed

2 files changed

+52
-9
lines changed

config/src/main/java/org/springframework/security/config/annotation/web/builders/WebSecurity.java

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -302,16 +302,18 @@ protected Filter performBuild() throws Exception {
302302
requestMatcherPrivilegeEvaluatorsEntries
303303
.add(getRequestMatcherPrivilegeEvaluatorsEntry(securityFilterChain));
304304
}
305-
boolean anyRequestConfigured = false;
305+
DefaultSecurityFilterChain anyRequestFilterChain = null;
306306
for (SecurityBuilder<? extends SecurityFilterChain> securityFilterChainBuilder : this.securityFilterChainBuilders) {
307307
SecurityFilterChain securityFilterChain = securityFilterChainBuilder.build();
308-
Assert.isTrue(!anyRequestConfigured,
309-
"A filter chain that matches any request has already been configured, which means that this filter chain ["
310-
+ securityFilterChain
311-
+ "] 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.");
308+
if (anyRequestFilterChain != null) {
309+
String message = "A filter chain that matches any request [" + anyRequestFilterChain
310+
+ "] has already been configured, which means that this filter chain [" + securityFilterChain
311+
+ "] 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.";
312+
throw new IllegalArgumentException(message);
313+
}
312314
if (securityFilterChain instanceof DefaultSecurityFilterChain defaultSecurityFilterChain) {
313315
if (defaultSecurityFilterChain.getRequestMatcher() instanceof AnyRequestMatcher) {
314-
anyRequestConfigured = true;
316+
anyRequestFilterChain = defaultSecurityFilterChain;
315317
}
316318
}
317319
securityFilterChains.add(securityFilterChain);

web/src/main/java/org/springframework/security/web/DefaultSecurityFilterChain.java

Lines changed: 44 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,14 @@
2424
import jakarta.servlet.http.HttpServletRequest;
2525
import org.apache.commons.logging.Log;
2626
import org.apache.commons.logging.LogFactory;
27+
import reactor.util.annotation.NonNull;
2728

29+
import org.springframework.beans.BeansException;
30+
import org.springframework.beans.factory.BeanFactory;
31+
import org.springframework.beans.factory.BeanFactoryAware;
32+
import org.springframework.beans.factory.BeanNameAware;
33+
import org.springframework.beans.factory.config.BeanDefinition;
34+
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
2835
import org.springframework.core.log.LogMessage;
2936
import org.springframework.security.web.util.matcher.RequestMatcher;
3037
import org.springframework.util.StringUtils;
@@ -36,14 +43,18 @@
3643
* @author Jinwoo Bae
3744
* @since 3.1
3845
*/
39-
public final class DefaultSecurityFilterChain implements SecurityFilterChain {
46+
public final class DefaultSecurityFilterChain implements SecurityFilterChain, BeanNameAware, BeanFactoryAware {
4047

4148
private static final Log logger = LogFactory.getLog(DefaultSecurityFilterChain.class);
4249

4350
private final RequestMatcher requestMatcher;
4451

4552
private final List<Filter> filters;
4653

54+
private String beanName;
55+
56+
private ConfigurableListableBeanFactory beanFactory;
57+
4758
public DefaultSecurityFilterChain(RequestMatcher requestMatcher, Filter... filters) {
4859
this(requestMatcher, Arrays.asList(filters));
4960
}
@@ -80,8 +91,38 @@ public boolean matches(HttpServletRequest request) {
8091

8192
@Override
8293
public String toString() {
83-
return this.getClass().getSimpleName() + " [RequestMatcher=" + this.requestMatcher + ", Filters=" + this.filters
84-
+ "]";
94+
List<String> filterNames = new ArrayList<>();
95+
for (Filter filter : this.filters) {
96+
String name = filter.getClass().getSimpleName();
97+
if (name.endsWith("Filter")) {
98+
name = name.substring(0, name.length() - "Filter".length());
99+
}
100+
filterNames.add(name);
101+
}
102+
String declaration = this.getClass().getSimpleName();
103+
if (this.beanName != null) {
104+
declaration += " defined as '" + this.beanName + "'";
105+
if (this.beanFactory != null) {
106+
BeanDefinition bd = this.beanFactory.getBeanDefinition(this.beanName);
107+
String description = bd.getResourceDescription();
108+
if (description != null) {
109+
declaration += " in [" + description + "]";
110+
}
111+
}
112+
}
113+
return declaration + " matching [" + this.requestMatcher + "] and having filters " + filterNames;
114+
}
115+
116+
@Override
117+
public void setBeanName(@NonNull String name) {
118+
this.beanName = name;
119+
}
120+
121+
@Override
122+
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
123+
if (beanFactory instanceof ConfigurableListableBeanFactory listable) {
124+
this.beanFactory = listable;
125+
}
85126
}
86127

87128
}

0 commit comments

Comments
 (0)