Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support checking AnyRequestMatcher securityFilterChains #15221

Merged
merged 1 commit into from
Jun 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* 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.
Expand Down Expand Up @@ -63,6 +63,7 @@
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;
Expand Down Expand Up @@ -296,8 +297,20 @@ protected Filter performBuild() throws Exception {
requestMatcherPrivilegeEvaluatorsEntries
.add(getRequestMatcherPrivilegeEvaluatorsEntry(securityFilterChain));
}
boolean anyRequestConfigured = false;
RequestMatcher matcher = null;
for (SecurityBuilder<? extends SecurityFilterChain> 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 for ["
+ matcher
+ "] 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 (securityFilterChain instanceof DefaultSecurityFilterChain defaultSecurityFilterChain) {
matcher = defaultSecurityFilterChain.getRequestMatcher();
if (matcher instanceof AnyRequestMatcher) {
anyRequestConfigured = true;
}
}
securityFilterChains.add(securityFilterChain);
requestMatcherPrivilegeEvaluatorsEntries
.add(getRequestMatcherPrivilegeEvaluatorsEntry(securityFilterChain));
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* 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.
Expand Down Expand Up @@ -318,6 +318,14 @@ public void loadConfigWhenMultipleSecurityFilterChainAndIgnoringThenWebInvocatio
assertThat(privilegeEvaluator.isAllowed("/ignoring1/child", null)).isTrue();
}

@Test
public void loadConfigWhenTwoSecurityFilterChainsPresentAndSecondWithAnyRequestThenException() {
assertThatExceptionOfType(BeanCreationException.class)
.isThrownBy(() -> this.spring.register(MultipleAnyRequestSecurityFilterChainConfig.class).autowire())
.havingRootCause()
.isExactlyInstanceOf(IllegalArgumentException.class);
}

private void assertAnotherUserPermission(WebInvocationPrivilegeEvaluator privilegeEvaluator) {
Authentication anotherUser = new TestingAuthenticationToken("anotherUser", "password", "ROLE_ANOTHER");
assertThat(privilegeEvaluator.isAllowed("/user", anotherUser)).isFalse();
Expand Down Expand Up @@ -819,4 +827,26 @@ public SecurityFilterChain permitAll(HttpSecurity http) throws Exception {

}

@Configuration
@EnableWebSecurity
@EnableWebMvc
@Import(AuthenticationTestConfiguration.class)
static class MultipleAnyRequestSecurityFilterChainConfig {

@Bean
@Order(0)
SecurityFilterChain api1(HttpSecurity http) throws Exception {
http.authorizeHttpRequests((auth) -> auth.anyRequest().authenticated());
return http.build();
}

@Bean
@Order(1)
SecurityFilterChain api2(HttpSecurity http) throws Exception {
http.securityMatcher("/app/**").authorizeHttpRequests((auth) -> auth.anyRequest().authenticated());
return http.build();
}

}

}