1
0
mirror of synced 2026-08-05 09:47:05 +00:00

Add Builder#authentication

This commit consolidates logic common to applying one
authenticaiton to another. Specifically, it will copy the
authorities in one authentication into the builder instance
of another.

Closes gh-18053
This commit is contained in:
Josh Cummings
2025-10-14 16:43:33 -06:00
parent e535e61c8b
commit 4102007119
8 changed files with 42 additions and 91 deletions
@@ -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<B extends Builder<B>> {
/**
* Apply this authentication instance
* <p>
* 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.
* </p>
* @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<String> 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}.
* <p>
@@ -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
*/
@@ -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<String> authorities = AuthorityUtils.authorityListToSet(result.getAuthorities());
assertThat(authorities).containsExactlyInAnyOrder("FACTOR_ONE", "FACTOR_TWO");
}
private static final class TestAbstractAuthenticationBuilder
extends TestingAuthenticationToken.Builder<TestAbstractAuthenticationBuilder> {
@@ -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();
@@ -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<String> 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);
@@ -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<String> 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);
@@ -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<String> 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);
@@ -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<String> 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();
@@ -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<String> 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));
}