Skip to content

Commit edde7db

Browse files
Merge branch 'spring-projects:main' into main
2 parents 9af1986 + fe9edc8 commit edde7db

File tree

76 files changed

+2789
-108
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

76 files changed

+2789
-108
lines changed

.github/dependabot.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ updates:
1919
- dependency-name: com.nimbusds:nimbus-jose-jwt
2020
- dependency-name: org.python:jython
2121
- dependency-name: org.apache.directory.server:*
22+
- dependency-name: org.apache.directory.shared:*
2223
- dependency-name: org.junit:junit-bom
2324
update-types:
2425
- version-update:semver-major
@@ -44,6 +45,7 @@ updates:
4445
- dependency-name: com.nimbusds:nimbus-jose-jwt
4546
- dependency-name: org.python:jython
4647
- dependency-name: org.apache.directory.server:*
48+
- dependency-name: org.apache.directory.shared:*
4749
- dependency-name: org.junit:junit-bom
4850
update-types:
4951
- version-update:semver-major
@@ -69,6 +71,7 @@ updates:
6971
- dependency-name: com.nimbusds:nimbus-jose-jwt
7072
- dependency-name: org.python:jython
7173
- dependency-name: org.apache.directory.server:*
74+
- dependency-name: org.apache.directory.shared:*
7275
- dependency-name: org.junit:junit-bom
7376
update-types:
7477
- version-update:semver-major

config/src/main/java/org/springframework/security/config/annotation/authentication/configuration/InitializeUserDetailsBeanManagerConfigurer.java

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -95,14 +95,10 @@ else if (beanNames.length > 1) {
9595
PasswordEncoder passwordEncoder = getBeanOrNull(PasswordEncoder.class);
9696
UserDetailsPasswordService passwordManager = getBeanOrNull(UserDetailsPasswordService.class);
9797
CompromisedPasswordChecker passwordChecker = getBeanOrNull(CompromisedPasswordChecker.class);
98-
DaoAuthenticationProvider provider;
98+
DaoAuthenticationProvider provider = new DaoAuthenticationProvider(userDetailsService);
9999
if (passwordEncoder != null) {
100-
provider = new DaoAuthenticationProvider(passwordEncoder);
100+
provider.setPasswordEncoder(passwordEncoder);
101101
}
102-
else {
103-
provider = new DaoAuthenticationProvider();
104-
}
105-
provider.setUserDetailsService(userDetailsService);
106102
if (passwordManager != null) {
107103
provider.setUserDetailsPasswordService(passwordManager);
108104
}

config/src/main/java/org/springframework/security/config/annotation/authentication/configurers/userdetails/AbstractDaoAuthenticationConfigurer.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@
3636
public abstract class AbstractDaoAuthenticationConfigurer<B extends ProviderManagerBuilder<B>, C extends AbstractDaoAuthenticationConfigurer<B, C, U>, U extends UserDetailsService>
3737
extends UserDetailsAwareConfigurer<B, U> {
3838

39-
private DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
39+
private DaoAuthenticationProvider provider;
4040

4141
private final U userDetailsService;
4242

@@ -46,7 +46,7 @@ public abstract class AbstractDaoAuthenticationConfigurer<B extends ProviderMana
4646
*/
4747
AbstractDaoAuthenticationConfigurer(U userDetailsService) {
4848
this.userDetailsService = userDetailsService;
49-
this.provider.setUserDetailsService(userDetailsService);
49+
this.provider = new DaoAuthenticationProvider(userDetailsService);
5050
if (userDetailsService instanceof UserDetailsPasswordService) {
5151
this.provider.setUserDetailsPasswordService((UserDetailsPasswordService) userDetailsService);
5252
}

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

Lines changed: 62 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,28 +18,45 @@
1818

1919
import java.util.List;
2020

21+
import jakarta.servlet.Filter;
22+
import org.apache.commons.logging.Log;
23+
import org.apache.commons.logging.LogFactory;
24+
2125
import org.springframework.security.web.DefaultSecurityFilterChain;
2226
import org.springframework.security.web.FilterChainProxy;
2327
import org.springframework.security.web.SecurityFilterChain;
28+
import org.springframework.security.web.UnreachableFilterChainException;
29+
import org.springframework.security.web.access.intercept.AuthorizationFilter;
30+
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
2431
import org.springframework.security.web.util.matcher.AnyRequestMatcher;
2532

2633
/**
2734
* A filter chain validator for filter chains built by {@link WebSecurity}
2835
*
36+
* @author Josh Cummings
37+
* @author Max Batischev
2938
* @since 6.5
3039
*/
3140
final class WebSecurityFilterChainValidator implements FilterChainProxy.FilterChainValidator {
3241

42+
private final Log logger = LogFactory.getLog(getClass());
43+
3344
@Override
3445
public void validate(FilterChainProxy filterChainProxy) {
3546
List<SecurityFilterChain> chains = filterChainProxy.getFilterChains();
47+
checkForAnyRequestRequestMatcher(chains);
48+
checkForDuplicateMatchers(chains);
49+
checkAuthorizationFilters(chains);
50+
}
51+
52+
private void checkForAnyRequestRequestMatcher(List<SecurityFilterChain> chains) {
3653
DefaultSecurityFilterChain anyRequestFilterChain = null;
3754
for (SecurityFilterChain chain : chains) {
3855
if (anyRequestFilterChain != null) {
3956
String message = "A filter chain that matches any request [" + anyRequestFilterChain
4057
+ "] has already been configured, which means that this filter chain [" + chain
4158
+ "] 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.";
42-
throw new IllegalArgumentException(message);
59+
throw new UnreachableFilterChainException(message, anyRequestFilterChain, chain);
4360
}
4461
if (chain instanceof DefaultSecurityFilterChain defaultChain) {
4562
if (defaultChain.getRequestMatcher() instanceof AnyRequestMatcher) {
@@ -49,4 +66,48 @@ public void validate(FilterChainProxy filterChainProxy) {
4966
}
5067
}
5168

69+
private void checkForDuplicateMatchers(List<SecurityFilterChain> chains) {
70+
DefaultSecurityFilterChain filterChain = null;
71+
for (SecurityFilterChain chain : chains) {
72+
if (filterChain != null) {
73+
if (chain instanceof DefaultSecurityFilterChain defaultChain) {
74+
if (defaultChain.getRequestMatcher().equals(filterChain.getRequestMatcher())) {
75+
throw new UnreachableFilterChainException(
76+
"The FilterChainProxy contains two filter chains using the" + " matcher "
77+
+ defaultChain.getRequestMatcher(),
78+
filterChain, defaultChain);
79+
}
80+
}
81+
}
82+
if (chain instanceof DefaultSecurityFilterChain defaultChain) {
83+
filterChain = defaultChain;
84+
}
85+
}
86+
}
87+
88+
private void checkAuthorizationFilters(List<SecurityFilterChain> chains) {
89+
Filter authorizationFilter = null;
90+
Filter filterSecurityInterceptor = null;
91+
for (SecurityFilterChain chain : chains) {
92+
for (Filter filter : chain.getFilters()) {
93+
if (filter instanceof AuthorizationFilter) {
94+
authorizationFilter = filter;
95+
}
96+
if (filter instanceof FilterSecurityInterceptor) {
97+
filterSecurityInterceptor = filter;
98+
}
99+
}
100+
if (authorizationFilter != null && filterSecurityInterceptor != null) {
101+
this.logger.warn(
102+
"It is not recommended to use authorizeRequests in the configuration. Please only use authorizeHttpRequests");
103+
}
104+
if (filterSecurityInterceptor != null) {
105+
this.logger.warn(
106+
"Usage of authorizeRequests is deprecated. Please use authorizeHttpRequests in the configuration");
107+
}
108+
authorizationFilter = null;
109+
filterSecurityInterceptor = null;
110+
}
111+
}
112+
52113
}

config/src/main/java/org/springframework/security/config/annotation/web/configurers/SessionManagementConfigurer.java

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
import org.springframework.security.web.authentication.session.RegisterSessionAuthenticationStrategy;
4848
import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;
4949
import org.springframework.security.web.authentication.session.SessionFixationProtectionStrategy;
50+
import org.springframework.security.web.authentication.session.SessionLimit;
5051
import org.springframework.security.web.context.DelegatingSecurityContextRepository;
5152
import org.springframework.security.web.context.HttpSessionSecurityContextRepository;
5253
import org.springframework.security.web.context.NullSecurityContextRepository;
@@ -123,7 +124,7 @@ public final class SessionManagementConfigurer<H extends HttpSecurityBuilder<H>>
123124

124125
private SessionRegistry sessionRegistry;
125126

126-
private Integer maximumSessions;
127+
private SessionLimit sessionLimit;
127128

128129
private String expiredUrl;
129130

@@ -329,7 +330,7 @@ public SessionManagementConfigurer<H> sessionFixation(
329330
* @return the {@link SessionManagementConfigurer} for further customizations
330331
*/
331332
public ConcurrencyControlConfigurer maximumSessions(int maximumSessions) {
332-
this.maximumSessions = maximumSessions;
333+
this.sessionLimit = SessionLimit.of(maximumSessions);
333334
this.propertiesThatRequireImplicitAuthentication.add("maximumSessions = " + maximumSessions);
334335
return new ConcurrencyControlConfigurer();
335336
}
@@ -570,7 +571,7 @@ private SessionAuthenticationStrategy getSessionAuthenticationStrategy(H http) {
570571
SessionRegistry sessionRegistry = getSessionRegistry(http);
571572
ConcurrentSessionControlAuthenticationStrategy concurrentSessionControlStrategy = new ConcurrentSessionControlAuthenticationStrategy(
572573
sessionRegistry);
573-
concurrentSessionControlStrategy.setMaximumSessions(this.maximumSessions);
574+
concurrentSessionControlStrategy.setMaximumSessions(this.sessionLimit);
574575
concurrentSessionControlStrategy.setExceptionIfMaximumExceeded(this.maxSessionsPreventsLogin);
575576
concurrentSessionControlStrategy = postProcess(concurrentSessionControlStrategy);
576577
RegisterSessionAuthenticationStrategy registerSessionStrategy = new RegisterSessionAuthenticationStrategy(
@@ -614,7 +615,7 @@ private void registerDelegateApplicationListener(H http, ApplicationListener<?>
614615
* @return
615616
*/
616617
private boolean isConcurrentSessionControlEnabled() {
617-
return this.maximumSessions != null;
618+
return this.sessionLimit != null;
618619
}
619620

620621
/**
@@ -706,7 +707,19 @@ private ConcurrencyControlConfigurer() {
706707
* @return the {@link ConcurrencyControlConfigurer} for further customizations
707708
*/
708709
public ConcurrencyControlConfigurer maximumSessions(int maximumSessions) {
709-
SessionManagementConfigurer.this.maximumSessions = maximumSessions;
710+
SessionManagementConfigurer.this.sessionLimit = SessionLimit.of(maximumSessions);
711+
return this;
712+
}
713+
714+
/**
715+
* Determines the behaviour when a session limit is detected.
716+
* @param sessionLimit the {@link SessionLimit} to check the maximum number of
717+
* sessions for a user
718+
* @return the {@link ConcurrencyControlConfigurer} for further customizations
719+
* @since 6.5
720+
*/
721+
public ConcurrencyControlConfigurer maximumSessions(SessionLimit sessionLimit) {
722+
SessionManagementConfigurer.this.sessionLimit = sessionLimit;
710723
return this;
711724
}
712725

config/src/main/java/org/springframework/security/config/authentication/AuthenticationManagerFactoryBean.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,7 @@ public AuthenticationManager getObject() throws Exception {
6565
if (uds == null) {
6666
throw new NoSuchBeanDefinitionException(BeanIds.AUTHENTICATION_MANAGER, MISSING_BEAN_ERROR_MESSAGE);
6767
}
68-
DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
69-
provider.setUserDetailsService(uds);
68+
DaoAuthenticationProvider provider = new DaoAuthenticationProvider(uds);
7069
PasswordEncoder passwordEncoder = this.bf.getBeanProvider(PasswordEncoder.class).getIfUnique();
7170
if (passwordEncoder != null) {
7271
provider.setPasswordEncoder(passwordEncoder);

config/src/main/java/org/springframework/security/config/http/DefaultFilterChainValidator.java

Lines changed: 49 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
import org.springframework.security.web.FilterChainProxy;
4040
import org.springframework.security.web.FilterInvocation;
4141
import org.springframework.security.web.SecurityFilterChain;
42+
import org.springframework.security.web.UnreachableFilterChainException;
4243
import org.springframework.security.web.access.ExceptionTranslationFilter;
4344
import org.springframework.security.web.access.intercept.AuthorizationFilter;
4445
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
@@ -53,7 +54,6 @@
5354
import org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter;
5455
import org.springframework.security.web.session.SessionManagementFilter;
5556
import org.springframework.security.web.util.matcher.AnyRequestMatcher;
56-
import org.springframework.security.web.util.matcher.RequestMatcher;
5757

5858
public class DefaultFilterChainValidator implements FilterChainProxy.FilterChainValidator {
5959

@@ -69,31 +69,67 @@ public void validate(FilterChainProxy fcp) {
6969
}
7070
checkPathOrder(new ArrayList<>(fcp.getFilterChains()));
7171
checkForDuplicateMatchers(new ArrayList<>(fcp.getFilterChains()));
72+
checkAuthorizationFilters(new ArrayList<>(fcp.getFilterChains()));
7273
}
7374

7475
private void checkPathOrder(List<SecurityFilterChain> filterChains) {
7576
// Check that the universal pattern is listed at the end, if at all
7677
Iterator<SecurityFilterChain> chains = filterChains.iterator();
7778
while (chains.hasNext()) {
78-
RequestMatcher matcher = ((DefaultSecurityFilterChain) chains.next()).getRequestMatcher();
79-
if (AnyRequestMatcher.INSTANCE.equals(matcher) && chains.hasNext()) {
80-
throw new IllegalArgumentException("A universal match pattern ('/**') is defined "
81-
+ " before other patterns in the filter chain, causing them to be ignored. Please check the "
82-
+ "ordering in your <security:http> namespace or FilterChainProxy bean configuration");
79+
if (chains.next() instanceof DefaultSecurityFilterChain securityFilterChain) {
80+
if (AnyRequestMatcher.INSTANCE.equals(securityFilterChain.getRequestMatcher()) && chains.hasNext()) {
81+
throw new UnreachableFilterChainException("A universal match pattern ('/**') is defined "
82+
+ " before other patterns in the filter chain, causing them to be ignored. Please check the "
83+
+ "ordering in your <security:http> namespace or FilterChainProxy bean configuration",
84+
securityFilterChain, chains.next());
85+
}
8386
}
8487
}
8588
}
8689

8790
private void checkForDuplicateMatchers(List<SecurityFilterChain> chains) {
88-
while (chains.size() > 1) {
89-
DefaultSecurityFilterChain chain = (DefaultSecurityFilterChain) chains.remove(0);
90-
for (SecurityFilterChain test : chains) {
91-
if (chain.getRequestMatcher().equals(((DefaultSecurityFilterChain) test).getRequestMatcher())) {
92-
throw new IllegalArgumentException("The FilterChainProxy contains two filter chains using the"
93-
+ " matcher " + chain.getRequestMatcher() + ". If you are using multiple <http> namespace "
94-
+ "elements, you must use a 'pattern' attribute to define the request patterns to which they apply.");
91+
DefaultSecurityFilterChain filterChain = null;
92+
for (SecurityFilterChain chain : chains) {
93+
if (filterChain != null) {
94+
if (chain instanceof DefaultSecurityFilterChain defaultChain) {
95+
if (defaultChain.getRequestMatcher().equals(filterChain.getRequestMatcher())) {
96+
throw new UnreachableFilterChainException(
97+
"The FilterChainProxy contains two filter chains using the" + " matcher "
98+
+ defaultChain.getRequestMatcher()
99+
+ ". If you are using multiple <http> namespace "
100+
+ "elements, you must use a 'pattern' attribute to define the request patterns to which they apply.",
101+
defaultChain, chain);
102+
}
95103
}
96104
}
105+
if (chain instanceof DefaultSecurityFilterChain defaultChain) {
106+
filterChain = defaultChain;
107+
}
108+
}
109+
}
110+
111+
private void checkAuthorizationFilters(List<SecurityFilterChain> chains) {
112+
Filter authorizationFilter = null;
113+
Filter filterSecurityInterceptor = null;
114+
for (SecurityFilterChain chain : chains) {
115+
for (Filter filter : chain.getFilters()) {
116+
if (filter instanceof AuthorizationFilter) {
117+
authorizationFilter = filter;
118+
}
119+
if (filter instanceof FilterSecurityInterceptor) {
120+
filterSecurityInterceptor = filter;
121+
}
122+
}
123+
if (authorizationFilter != null && filterSecurityInterceptor != null) {
124+
this.logger.warn(
125+
"It is not recommended to use authorizeRequests in the configuration. Please only use authorizeHttpRequests");
126+
}
127+
if (filterSecurityInterceptor != null) {
128+
this.logger.warn(
129+
"Usage of authorizeRequests is deprecated. Please use authorizeHttpRequests in the configuration");
130+
}
131+
authorizationFilter = null;
132+
filterSecurityInterceptor = null;
97133
}
98134
}
99135

config/src/main/java/org/springframework/security/config/http/HttpConfigurationBuilder.java

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,10 @@ class HttpConfigurationBuilder {
122122

123123
private static final String ATT_SESSION_AUTH_STRATEGY_REF = "session-authentication-strategy-ref";
124124

125+
private static final String ATT_MAX_SESSIONS_REF = "max-sessions-ref";
126+
127+
private static final String ATT_MAX_SESSIONS = "max-sessions";
128+
125129
private static final String ATT_SESSION_AUTH_ERROR_URL = "session-authentication-error-url";
126130

127131
private static final String ATT_SECURITY_CONTEXT_HOLDER_STRATEGY = "security-context-holder-strategy-ref";
@@ -485,10 +489,16 @@ else if (StringUtils.hasText(sessionAuthStratRef)) {
485489
concurrentSessionStrategy.addConstructorArgValue(this.sessionRegistryRef);
486490
String maxSessions = this.pc.getReaderContext()
487491
.getEnvironment()
488-
.resolvePlaceholders(sessionCtrlElt.getAttribute("max-sessions"));
492+
.resolvePlaceholders(sessionCtrlElt.getAttribute(ATT_MAX_SESSIONS));
489493
if (StringUtils.hasText(maxSessions)) {
490494
concurrentSessionStrategy.addPropertyValue("maximumSessions", maxSessions);
491495
}
496+
String maxSessionsRef = this.pc.getReaderContext()
497+
.getEnvironment()
498+
.resolvePlaceholders(sessionCtrlElt.getAttribute(ATT_MAX_SESSIONS_REF));
499+
if (StringUtils.hasText(maxSessionsRef)) {
500+
concurrentSessionStrategy.addPropertyReference("maximumSessions", maxSessionsRef);
501+
}
492502
String exceptionIfMaximumExceeded = sessionCtrlElt.getAttribute("error-if-maximum-exceeded");
493503
if (StringUtils.hasText(exceptionIfMaximumExceeded)) {
494504
concurrentSessionStrategy.addPropertyValue("exceptionIfMaximumExceeded", exceptionIfMaximumExceeded);
@@ -591,6 +601,12 @@ private void createConcurrencyControlFilterAndSessionRegistry(Element element) {
591601
.error("Cannot use 'expired-url' attribute and 'expired-session-strategy-ref'" + " attribute together.",
592602
source);
593603
}
604+
String maxSessions = element.getAttribute(ATT_MAX_SESSIONS);
605+
String maxSessionsRef = element.getAttribute(ATT_MAX_SESSIONS_REF);
606+
if (StringUtils.hasText(maxSessions) && StringUtils.hasText(maxSessionsRef)) {
607+
this.pc.getReaderContext()
608+
.error("Cannot use 'max-sessions' attribute and 'max-sessions-ref' attribute together.", source);
609+
}
594610
if (StringUtils.hasText(expiryUrl)) {
595611
BeanDefinitionBuilder expiredSessionBldr = BeanDefinitionBuilder
596612
.rootBeanDefinition(SimpleRedirectSessionInformationExpiredStrategy.class);

config/src/main/java/org/springframework/security/config/http/Saml2LogoutBeanDefinitionParser.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,7 @@ public BeanDefinition parse(Element element, ParserContext pc) {
146146
BeanMetadataElement saml2LogoutRequestSuccessHandler = BeanDefinitionBuilder
147147
.rootBeanDefinition(Saml2RelyingPartyInitiatedLogoutSuccessHandler.class)
148148
.addConstructorArgValue(logoutRequestResolver)
149+
.addPropertyValue("logoutRequestRepository", logoutRequestRepository)
149150
.getBeanDefinition();
150151
this.logoutFilter = BeanDefinitionBuilder.rootBeanDefinition(LogoutFilter.class)
151152
.addConstructorArgValue(saml2LogoutRequestSuccessHandler)

0 commit comments

Comments
 (0)