diff --git a/core/src/main/java/org/springframework/security/core/BuildableAuthentication.java b/core/src/main/java/org/springframework/security/core/BuildableAuthentication.java index b4ce0d3c34..577c6a9456 100644 --- a/core/src/main/java/org/springframework/security/core/BuildableAuthentication.java +++ b/core/src/main/java/org/springframework/security/core/BuildableAuthentication.java @@ -17,7 +17,6 @@ package org.springframework.security.core; import java.util.Collection; -import java.util.HashSet; import java.util.Set; import java.util.function.Consumer; import java.util.stream.Collectors; @@ -84,6 +83,30 @@ public interface BuildableAuthentication extends Authentication { */ interface Builder> { + /** + * Apply this authentication instance + *

+ * By default, merges the authorities in the provided {@code authentication} with + * the authentication being built. Only those authorities that haven't already + * been specified to the builder will be added. + *

+ * @param authentication the {@link Authentication} to appluy + * @return the {@link Builder} for additional configuration + * @see BuildableAuthentication#getAuthorities + */ + default B authentication(Authentication authentication) { + return authorities((a) -> { + Set newAuthorities = a.stream() + .map(GrantedAuthority::getAuthority) + .collect(Collectors.toUnmodifiableSet()); + for (GrantedAuthority currentAuthority : authentication.getAuthorities()) { + if (!newAuthorities.contains(currentAuthority.getAuthority())) { + a.add(currentAuthority); + } + } + }); + } + /** * Mutate the authorities with this {@link Consumer}. *

@@ -145,8 +168,8 @@ public interface BuildableAuthentication extends Authentication { /** * Mark this authentication as authenticated or not - * @param authenticated whether this is an authenticated - * {@link Authentication} instance + * @param authenticated whether this is an authenticated {@link Authentication} + * instance * @return the {@link Builder} for additional configuration * @see Authentication#isAuthenticated */ diff --git a/core/src/test/java/org/springframework/security/authentication/AbstractAuthenticationBuilderTests.java b/core/src/test/java/org/springframework/security/authentication/AbstractAuthenticationBuilderTests.java index 6bd66b7166..15b61d6f46 100644 --- a/core/src/test/java/org/springframework/security/authentication/AbstractAuthenticationBuilderTests.java +++ b/core/src/test/java/org/springframework/security/authentication/AbstractAuthenticationBuilderTests.java @@ -37,6 +37,16 @@ class AbstractAuthenticationBuilderTests { assertThat(authorities).containsExactlyInAnyOrder("FACTOR_ONE", "FACTOR_TWO"); } + @Test + void authenticationWhenAuthoritiesThenAdds() { + TestingAuthenticationToken factorOne = new TestingAuthenticationToken("user", "pass", "FACTOR_ONE"); + TestingAuthenticationToken factorTwo = new TestingAuthenticationToken("user", "pass", "FACTOR_TWO"); + TestAbstractAuthenticationBuilder builder = new TestAbstractAuthenticationBuilder(factorOne); + Authentication result = builder.authentication(factorTwo).build(); + Set authorities = AuthorityUtils.authorityListToSet(result.getAuthorities()); + assertThat(authorities).containsExactlyInAnyOrder("FACTOR_ONE", "FACTOR_TWO"); + } + private static final class TestAbstractAuthenticationBuilder extends TestingAuthenticationToken.Builder { diff --git a/oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/web/authentication/BearerTokenAuthenticationFilter.java b/oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/web/authentication/BearerTokenAuthenticationFilter.java index 88366dca7f..1bc65bbd37 100644 --- a/oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/web/authentication/BearerTokenAuthenticationFilter.java +++ b/oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/web/authentication/BearerTokenAuthenticationFilter.java @@ -184,9 +184,7 @@ public class BearerTokenAuthenticationFilter extends OncePerRequestFilter { Authentication current = this.securityContextHolderStrategy.getContext().getAuthentication(); if (current != null && current.isAuthenticated()) { if (authenticationResult instanceof BuildableAuthentication buildable) { - authenticationResult = buildable.toBuilder() - .authorities((a) -> a.addAll(current.getAuthorities())) - .build(); + authenticationResult = buildable.toBuilder().authentication(current).build(); } } SecurityContext context = this.securityContextHolderStrategy.createEmptyContext(); diff --git a/web/src/main/java/org/springframework/security/web/authentication/AbstractAuthenticationProcessingFilter.java b/web/src/main/java/org/springframework/security/web/authentication/AbstractAuthenticationProcessingFilter.java index 6f90f475cc..a7113235df 100644 --- a/web/src/main/java/org/springframework/security/web/authentication/AbstractAuthenticationProcessingFilter.java +++ b/web/src/main/java/org/springframework/security/web/authentication/AbstractAuthenticationProcessingFilter.java @@ -17,8 +17,6 @@ package org.springframework.security.web.authentication; import java.io.IOException; -import java.util.Set; -import java.util.stream.Collectors; import jakarta.servlet.FilterChain; import jakarta.servlet.ServletException; @@ -42,7 +40,6 @@ import org.springframework.security.authentication.event.InteractiveAuthenticati import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; import org.springframework.security.core.BuildableAuthentication; -import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.SpringSecurityMessageSource; import org.springframework.security.core.context.SecurityContext; import org.springframework.security.core.context.SecurityContextHolder; @@ -255,20 +252,7 @@ public abstract class AbstractAuthenticationProcessingFilter extends GenericFilt Authentication current = this.securityContextHolderStrategy.getContext().getAuthentication(); if (current != null && current.isAuthenticated()) { if (authenticationResult instanceof BuildableAuthentication buildable) { - authenticationResult = buildable.toBuilder() - // @formatter:off - .authorities((a) -> { - Set newAuthorities = a.stream() - .map(GrantedAuthority::getAuthority) - .collect(Collectors.toUnmodifiableSet()); - for (GrantedAuthority currentAuthority : current.getAuthorities()) { - if (!newAuthorities.contains(currentAuthority.getAuthority())) { - a.add(currentAuthority); - } - } - }) - .build(); - // @formatter:on + authenticationResult = buildable.toBuilder().authentication(current).build(); } } this.sessionStrategy.onAuthentication(authenticationResult, request, response); diff --git a/web/src/main/java/org/springframework/security/web/authentication/AuthenticationFilter.java b/web/src/main/java/org/springframework/security/web/authentication/AuthenticationFilter.java index 40ef7f4951..17c32292f7 100644 --- a/web/src/main/java/org/springframework/security/web/authentication/AuthenticationFilter.java +++ b/web/src/main/java/org/springframework/security/web/authentication/AuthenticationFilter.java @@ -17,8 +17,6 @@ package org.springframework.security.web.authentication; import java.io.IOException; -import java.util.Set; -import java.util.stream.Collectors; import jakarta.servlet.Filter; import jakarta.servlet.FilterChain; @@ -34,7 +32,6 @@ import org.springframework.security.authentication.AuthenticationManagerResolver import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; import org.springframework.security.core.BuildableAuthentication; -import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.context.SecurityContext; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.context.SecurityContextHolderStrategy; @@ -191,20 +188,7 @@ public class AuthenticationFilter extends OncePerRequestFilter { Authentication current = this.securityContextHolderStrategy.getContext().getAuthentication(); if (current != null && current.isAuthenticated()) { if (authenticationResult instanceof BuildableAuthentication buildable) { - authenticationResult = buildable.toBuilder() - // @formatter:off - .authorities((a) -> { - Set newAuthorities = a.stream() - .map(GrantedAuthority::getAuthority) - .collect(Collectors.toUnmodifiableSet()); - for (GrantedAuthority currentAuthority : current.getAuthorities()) { - if (!newAuthorities.contains(currentAuthority.getAuthority())) { - a.add(currentAuthority); - } - } - }) - .build(); - // @formatter:on + authenticationResult = buildable.toBuilder().authentication(current).build(); } } HttpSession session = request.getSession(false); diff --git a/web/src/main/java/org/springframework/security/web/authentication/preauth/AbstractPreAuthenticatedProcessingFilter.java b/web/src/main/java/org/springframework/security/web/authentication/preauth/AbstractPreAuthenticatedProcessingFilter.java index dab00ce0f1..9da11bbaf4 100755 --- a/web/src/main/java/org/springframework/security/web/authentication/preauth/AbstractPreAuthenticatedProcessingFilter.java +++ b/web/src/main/java/org/springframework/security/web/authentication/preauth/AbstractPreAuthenticatedProcessingFilter.java @@ -17,8 +17,6 @@ package org.springframework.security.web.authentication.preauth; import java.io.IOException; -import java.util.Set; -import java.util.stream.Collectors; import jakarta.servlet.FilterChain; import jakarta.servlet.ServletException; @@ -38,7 +36,6 @@ import org.springframework.security.authentication.event.InteractiveAuthenticati import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; import org.springframework.security.core.BuildableAuthentication; -import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.context.SecurityContext; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.context.SecurityContextHolderStrategy; @@ -211,20 +208,7 @@ public abstract class AbstractPreAuthenticatedProcessingFilter extends GenericFi Authentication current = this.securityContextHolderStrategy.getContext().getAuthentication(); if (current != null && current.isAuthenticated()) { if (authenticationResult instanceof BuildableAuthentication buildable) { - authenticationResult = buildable.toBuilder() - // @formatter:off - .authorities((a) -> { - Set newAuthorities = a.stream() - .map(GrantedAuthority::getAuthority) - .collect(Collectors.toUnmodifiableSet()); - for (GrantedAuthority currentAuthority : current.getAuthorities()) { - if (!newAuthorities.contains(currentAuthority.getAuthority())) { - a.add(currentAuthority); - } - } - }) - .build(); - // @formatter:on + authenticationResult = buildable.toBuilder().authentication(current).build(); } } successfulAuthentication(request, response, authenticationResult); diff --git a/web/src/main/java/org/springframework/security/web/authentication/www/BasicAuthenticationFilter.java b/web/src/main/java/org/springframework/security/web/authentication/www/BasicAuthenticationFilter.java index 92b96e8274..db542b2196 100644 --- a/web/src/main/java/org/springframework/security/web/authentication/www/BasicAuthenticationFilter.java +++ b/web/src/main/java/org/springframework/security/web/authentication/www/BasicAuthenticationFilter.java @@ -18,8 +18,6 @@ package org.springframework.security.web.authentication.www; import java.io.IOException; import java.nio.charset.Charset; -import java.util.Set; -import java.util.stream.Collectors; import jakarta.servlet.FilterChain; import jakarta.servlet.ServletException; @@ -34,7 +32,6 @@ import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; import org.springframework.security.core.BuildableAuthentication; -import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.context.SecurityContext; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.context.SecurityContextHolderStrategy; @@ -193,20 +190,7 @@ public class BasicAuthenticationFilter extends OncePerRequestFilter { Authentication current = this.securityContextHolderStrategy.getContext().getAuthentication(); if (current != null && current.isAuthenticated()) { if (authResult instanceof BuildableAuthentication buildable) { - authResult = buildable.toBuilder() - // @formatter:off - .authorities((a) -> { - Set newAuthorities = a.stream() - .map(GrantedAuthority::getAuthority) - .collect(Collectors.toUnmodifiableSet()); - for (GrantedAuthority currentAuthority : current.getAuthorities()) { - if (!newAuthorities.contains(currentAuthority.getAuthority())) { - a.add(currentAuthority); - } - } - }) - .build(); - // @formatter:on + authResult = buildable.toBuilder().authentication(current).build(); } } SecurityContext context = this.securityContextHolderStrategy.createEmptyContext(); diff --git a/web/src/main/java/org/springframework/security/web/server/authentication/AuthenticationWebFilter.java b/web/src/main/java/org/springframework/security/web/server/authentication/AuthenticationWebFilter.java index 207783223a..fa89400309 100644 --- a/web/src/main/java/org/springframework/security/web/server/authentication/AuthenticationWebFilter.java +++ b/web/src/main/java/org/springframework/security/web/server/authentication/AuthenticationWebFilter.java @@ -16,9 +16,7 @@ package org.springframework.security.web.server.authentication; -import java.util.Set; import java.util.function.Function; -import java.util.stream.Collectors; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -30,7 +28,6 @@ import org.springframework.security.authentication.ReactiveAuthenticationManager import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; import org.springframework.security.core.BuildableAuthentication; -import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.context.ReactiveSecurityContextHolder; import org.springframework.security.core.context.SecurityContextImpl; import org.springframework.security.web.server.WebFilterExchange; @@ -145,20 +142,7 @@ public class AuthenticationWebFilter implements WebFilter { if (!(result instanceof BuildableAuthentication buildable)) { return result; } - return buildable.toBuilder() - // @formatter:off - .authorities((a) -> { - Set newAuthorities = a.stream() - .map(GrantedAuthority::getAuthority) - .collect(Collectors.toUnmodifiableSet()); - for (GrantedAuthority currentAuthority : current.getAuthorities()) { - if (!newAuthorities.contains(currentAuthority.getAuthority())) { - a.add(currentAuthority); - } - } - }) - .build(); - // @formatter:on + return buildable.toBuilder().authentication(current).build(); }).switchIfEmpty(Mono.just(result)); }