diff --git a/core/src/test/java/org/springframework/security/authentication/NonBuildableAuthenticationToken.java b/core/src/test/java/org/springframework/security/authentication/NonBuildableAuthenticationToken.java
new file mode 100644
index 0000000000..8099b826f3
--- /dev/null
+++ b/core/src/test/java/org/springframework/security/authentication/NonBuildableAuthenticationToken.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2004-present 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.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.security.authentication;
+
+public class NonBuildableAuthenticationToken extends TestingAuthenticationToken {
+
+ public NonBuildableAuthenticationToken(String user, String password, String... authorities) {
+ super(user, password, authorities);
+ }
+
+}
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 740b77ad6d..6541b5aa7a 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
@@ -17,6 +17,7 @@
package org.springframework.security.oauth2.server.resource.web.authentication;
import java.io.IOException;
+import java.lang.reflect.Method;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
@@ -56,7 +57,6 @@ import org.springframework.security.web.context.RequestAttributeSecurityContextR
import org.springframework.security.web.context.SecurityContextRepository;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
-import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;
@@ -214,7 +214,12 @@ public class BearerTokenAuthenticationFilter extends OncePerRequestFilter {
}
private static boolean declaresToBuilder(Authentication authentication) {
- return ReflectionUtils.findMethod(authentication.getClass(), "toBuilder") != null;
+ for (Method method : authentication.getClass().getDeclaredMethods()) {
+ if (method.getName().equals("toBuilder") && method.getParameterTypes().length == 0) {
+ return true;
+ }
+ }
+ return false;
}
/**
diff --git a/oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/web/authentication/BearerTokenAuthenticationFilterTests.java b/oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/web/authentication/BearerTokenAuthenticationFilterTests.java
index bdfed6bbcd..b4939bb9e8 100644
--- a/oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/web/authentication/BearerTokenAuthenticationFilterTests.java
+++ b/oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/web/authentication/BearerTokenAuthenticationFilterTests.java
@@ -38,6 +38,7 @@ import org.springframework.security.authentication.AuthenticationDetailsSource;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationManagerResolver;
import org.springframework.security.authentication.AuthenticationServiceException;
+import org.springframework.security.authentication.NonBuildableAuthenticationToken;
import org.springframework.security.authentication.SecurityAssertions;
import org.springframework.security.authentication.TestingAuthenticationToken;
import org.springframework.security.core.Authentication;
@@ -316,6 +317,24 @@ public class BearerTokenAuthenticationFilterTests {
// @formatter:on
}
+ @Test
+ void doFilterWhenNonBuildableAuthenticationSubclassThenSkipsToBuilder() throws Exception {
+ TestingAuthenticationToken existingAuthn = new TestingAuthenticationToken("username", "password", "FACTORONE");
+ SecurityContextHolder.setContext(new SecurityContextImpl(existingAuthn));
+ given(this.authenticationManager.authenticate(any()))
+ .willReturn(new NonBuildableAuthenticationToken("username", "password", "FACTORTWO"));
+ given(this.bearerTokenResolver.resolve(any())).willReturn("token");
+ BearerTokenAuthenticationFilter filter = addMocks(
+ new BearerTokenAuthenticationFilter(this.authenticationManager));
+ filter.doFilter(this.request, this.response, this.filterChain);
+ Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
+ // @formatter:off
+ SecurityAssertions.assertThat(authentication).authorities()
+ .extracting(GrantedAuthority::getAuthority)
+ .containsExactly("FACTORTWO");
+ // @formatter:on
+ }
+
@Test
public void setAuthenticationEntryPointWhenNullThenThrowsException() {
BearerTokenAuthenticationFilter filter = new BearerTokenAuthenticationFilter(this.authenticationManager);
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 19c1dca43a..42e9d0ce5a 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,6 +17,7 @@
package org.springframework.security.web.authentication;
import java.io.IOException;
+import java.lang.reflect.Method;
import java.util.Set;
import java.util.stream.Collectors;
@@ -252,7 +253,7 @@ public abstract class AbstractAuthenticationProcessingFilter extends GenericFilt
return;
}
Authentication current = this.securityContextHolderStrategy.getContext().getAuthentication();
- if (current != null && current.isAuthenticated()) {
+ if (current != null && current.isAuthenticated() && declaresToBuilder(authenticationResult)) {
authenticationResult = authenticationResult.toBuilder()
// @formatter:off
.authorities((a) -> {
@@ -285,6 +286,15 @@ public abstract class AbstractAuthenticationProcessingFilter extends GenericFilt
}
}
+ private static boolean declaresToBuilder(Authentication authentication) {
+ for (Method method : authentication.getClass().getDeclaredMethods()) {
+ if (method.getName().equals("toBuilder") && method.getParameterTypes().length == 0) {
+ return true;
+ }
+ }
+ return false;
+ }
+
/**
* Indicates whether this filter should attempt to process a login request for the
* current invocation.
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 c1d6d08a24..0425405200 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,6 +17,7 @@
package org.springframework.security.web.authentication;
import java.io.IOException;
+import java.lang.reflect.Method;
import java.util.Set;
import java.util.stream.Collectors;
@@ -188,7 +189,7 @@ public class AuthenticationFilter extends OncePerRequestFilter {
return;
}
Authentication current = this.securityContextHolderStrategy.getContext().getAuthentication();
- if (current != null && current.isAuthenticated()) {
+ if (current != null && current.isAuthenticated() && declaresToBuilder(authenticationResult)) {
authenticationResult = authenticationResult.toBuilder()
// @formatter:off
.authorities((a) -> {
@@ -215,6 +216,15 @@ public class AuthenticationFilter extends OncePerRequestFilter {
}
}
+ private static boolean declaresToBuilder(Authentication authentication) {
+ for (Method method : authentication.getClass().getDeclaredMethods()) {
+ if (method.getName().equals("toBuilder") && method.getParameterTypes().length == 0) {
+ return true;
+ }
+ }
+ return false;
+ }
+
@Override
protected String getAlreadyFilteredAttributeName() {
String name = getFilterName();
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 3d8f8acc3f..7f0322cf6a 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,6 +17,7 @@
package org.springframework.security.web.authentication.preauth;
import java.io.IOException;
+import java.lang.reflect.Method;
import java.util.Set;
import java.util.stream.Collectors;
@@ -208,7 +209,7 @@ public abstract class AbstractPreAuthenticatedProcessingFilter extends GenericFi
authenticationRequest.setDetails(this.authenticationDetailsSource.buildDetails(request));
Authentication authenticationResult = this.authenticationManager.authenticate(authenticationRequest);
Authentication current = this.securityContextHolderStrategy.getContext().getAuthentication();
- if (current != null && current.isAuthenticated()) {
+ if (current != null && current.isAuthenticated() && declaresToBuilder(authenticationResult)) {
authenticationResult = authenticationResult.toBuilder()
// @formatter:off
.authorities((a) -> {
@@ -234,6 +235,15 @@ public abstract class AbstractPreAuthenticatedProcessingFilter extends GenericFi
}
}
+ private static boolean declaresToBuilder(Authentication authentication) {
+ for (Method method : authentication.getClass().getDeclaredMethods()) {
+ if (method.getName().equals("toBuilder") && method.getParameterTypes().length == 0) {
+ return true;
+ }
+ }
+ return false;
+ }
+
/**
* Puts the Authentication instance returned by the authentication
* manager into the secure context.
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 e2f6435e78..8e679b82ef 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
@@ -17,6 +17,7 @@
package org.springframework.security.web.authentication.www;
import java.io.IOException;
+import java.lang.reflect.Method;
import java.nio.charset.Charset;
import java.util.Set;
import java.util.stream.Collectors;
@@ -190,7 +191,7 @@ public class BasicAuthenticationFilter extends OncePerRequestFilter {
if (authenticationIsRequired(username)) {
Authentication authResult = this.authenticationManager.authenticate(authRequest);
Authentication current = this.securityContextHolderStrategy.getContext().getAuthentication();
- if (current != null && current.isAuthenticated()) {
+ if (current != null && current.isAuthenticated() && declaresToBuilder(authResult)) {
authResult = authResult.toBuilder()
// @formatter:off
.authorities((a) -> {
@@ -234,6 +235,15 @@ public class BasicAuthenticationFilter extends OncePerRequestFilter {
chain.doFilter(request, response);
}
+ private static boolean declaresToBuilder(Authentication authentication) {
+ for (Method method : authentication.getClass().getDeclaredMethods()) {
+ if (method.getName().equals("toBuilder") && method.getParameterTypes().length == 0) {
+ return true;
+ }
+ }
+ return false;
+ }
+
protected boolean authenticationIsRequired(String username) {
// Only reauthenticate if username doesn't match SecurityContextHolder and user
// isn't authenticated (see SEC-53)
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 47d06ee1ac..528ab64d54 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,6 +16,7 @@
package org.springframework.security.web.server.authentication;
+import java.lang.reflect.Method;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;
@@ -141,6 +142,9 @@ public class AuthenticationWebFilter implements WebFilter {
if (!current.isAuthenticated()) {
return result;
}
+ if (!declaresToBuilder(result)) {
+ return result;
+ }
return result.toBuilder()
// @formatter:off
.authorities((a) -> {
@@ -158,6 +162,15 @@ public class AuthenticationWebFilter implements WebFilter {
}).switchIfEmpty(Mono.just(result));
}
+ private static boolean declaresToBuilder(Authentication authentication) {
+ for (Method method : authentication.getClass().getDeclaredMethods()) {
+ if (method.getName().equals("toBuilder") && method.getParameterTypes().length == 0) {
+ return true;
+ }
+ }
+ return false;
+ }
+
protected Mono onAuthenticationSuccess(Authentication authentication, WebFilterExchange webFilterExchange) {
ServerWebExchange exchange = webFilterExchange.getExchange();
SecurityContextImpl securityContext = new SecurityContextImpl();
diff --git a/web/src/test/java/org/springframework/security/web/authentication/AbstractAuthenticationProcessingFilterTests.java b/web/src/test/java/org/springframework/security/web/authentication/AbstractAuthenticationProcessingFilterTests.java
index f57f601695..5ecd64ac9d 100644
--- a/web/src/test/java/org/springframework/security/web/authentication/AbstractAuthenticationProcessingFilterTests.java
+++ b/web/src/test/java/org/springframework/security/web/authentication/AbstractAuthenticationProcessingFilterTests.java
@@ -37,6 +37,8 @@ import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.InternalAuthenticationServiceException;
+import org.springframework.security.authentication.NonBuildableAuthenticationToken;
+import org.springframework.security.authentication.SecurityAssertions;
import org.springframework.security.authentication.TestAuthentication;
import org.springframework.security.authentication.TestingAuthenticationToken;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
@@ -480,6 +482,22 @@ public class AbstractAuthenticationProcessingFilterTests {
.containsExactly(DefaultEqualsGrantedAuthority.AUTHORITY);
}
+ @Test
+ void doFilterWhenNotOverridingToBuilderThenDoesNotMergeAuthorities() throws Exception {
+ TestingAuthenticationToken existingAuthn = new TestingAuthenticationToken("username", "password", "FACTORONE");
+ SecurityContextHolder.setContext(new SecurityContextImpl(existingAuthn));
+ MockHttpServletRequest request = createMockAuthenticationRequest();
+ MockHttpServletResponse response = new MockHttpServletResponse();
+ MockAuthenticationFilter filter = new MockAuthenticationFilter(
+ new NonBuildableAuthenticationToken("username", "password", "FACTORTWO"));
+ filter.doFilter(request, response, new MockFilterChain(false));
+ Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
+ SecurityAssertions.assertThat(authentication)
+ .authorities()
+ .extracting(GrantedAuthority::getAuthority)
+ .containsExactly("FACTORTWO");
+ }
+
/**
* https://github.com/spring-projects/spring-security/pull/3905
*/
diff --git a/web/src/test/java/org/springframework/security/web/authentication/AuthenticationFilterTests.java b/web/src/test/java/org/springframework/security/web/authentication/AuthenticationFilterTests.java
index 9788ccf2f8..c3dd84c11a 100644
--- a/web/src/test/java/org/springframework/security/web/authentication/AuthenticationFilterTests.java
+++ b/web/src/test/java/org/springframework/security/web/authentication/AuthenticationFilterTests.java
@@ -37,6 +37,8 @@ import org.springframework.mock.web.MockHttpSession;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationManagerResolver;
import org.springframework.security.authentication.BadCredentialsException;
+import org.springframework.security.authentication.NonBuildableAuthenticationToken;
+import org.springframework.security.authentication.SecurityAssertions;
import org.springframework.security.authentication.TestingAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
@@ -348,6 +350,26 @@ public class AuthenticationFilterTests {
.containsExactlyInAnyOrder(DefaultEqualsGrantedAuthority.AUTHORITY);
}
+ @Test
+ void doFilterWhenNotOverridingToBuilderThenDoesNotMergeAuthorities() throws Exception {
+ TestingAuthenticationToken existingAuthn = new TestingAuthenticationToken("username", "password", "FACTORONE");
+ SecurityContextHolder.setContext(new SecurityContextImpl(existingAuthn));
+ given(this.authenticationConverter.convert(any())).willReturn(existingAuthn);
+ given(this.authenticationManager.authenticate(any()))
+ .willReturn(new NonBuildableAuthenticationToken("user", "password", "FACTORTWO"));
+ MockHttpServletRequest request = new MockHttpServletRequest("GET", "/");
+ MockHttpServletResponse response = new MockHttpServletResponse();
+ FilterChain chain = new MockFilterChain();
+ AuthenticationFilter filter = new AuthenticationFilter(this.authenticationManager,
+ this.authenticationConverter);
+ filter.doFilter(request, response, chain);
+ Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
+ SecurityAssertions.assertThat(authentication)
+ .authorities()
+ .extracting(GrantedAuthority::getAuthority)
+ .containsExactly("FACTORTWO");
+ }
+
@Test
public void filterWhenCustomSecurityContextRepositoryAndSuccessfulAuthenticationRepositoryUsed() throws Exception {
SecurityContextRepository securityContextRepository = mock(SecurityContextRepository.class);
diff --git a/web/src/test/java/org/springframework/security/web/authentication/preauth/AbstractPreAuthenticatedProcessingFilterTests.java b/web/src/test/java/org/springframework/security/web/authentication/preauth/AbstractPreAuthenticatedProcessingFilterTests.java
index ebf578e4db..834acab5ce 100644
--- a/web/src/test/java/org/springframework/security/web/authentication/preauth/AbstractPreAuthenticatedProcessingFilterTests.java
+++ b/web/src/test/java/org/springframework/security/web/authentication/preauth/AbstractPreAuthenticatedProcessingFilterTests.java
@@ -32,6 +32,8 @@ import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
+import org.springframework.security.authentication.NonBuildableAuthenticationToken;
+import org.springframework.security.authentication.SecurityAssertions;
import org.springframework.security.authentication.TestingAuthenticationToken;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
@@ -436,6 +438,22 @@ public class AbstractPreAuthenticatedProcessingFilterTests {
// @formatter:on
}
+ @Test
+ void doFilterWhenNotOverridingToBuilderThenDoesNotMergeAuthorities() throws Exception {
+ TestingAuthenticationToken existingAuthn = new TestingAuthenticationToken("username", "password", "FACTORONE");
+ SecurityContextHolder.setContext(new SecurityContextImpl(existingAuthn));
+ MockHttpServletRequest request = new MockHttpServletRequest();
+ MockHttpServletResponse response = new MockHttpServletResponse();
+ this.filter = createFilterAuthenticatesWith(
+ new NonBuildableAuthenticationToken("username", "password", "FACTORTWO"));
+ this.filter.doFilter(request, response, new MockFilterChain());
+ Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
+ SecurityAssertions.assertThat(authentication)
+ .authorities()
+ .extracting(GrantedAuthority::getAuthority)
+ .containsExactly("FACTORTWO");
+ }
+
private AbstractPreAuthenticatedProcessingFilter createFilterAuthenticatesWith(Authentication authentication) {
ConcretePreAuthenticatedProcessingFilter filter = new ConcretePreAuthenticatedProcessingFilter();
filter.setRequiresAuthenticationRequestMatcher(AnyRequestMatcher.INSTANCE);
diff --git a/web/src/test/java/org/springframework/security/web/authentication/www/BasicAuthenticationFilterTests.java b/web/src/test/java/org/springframework/security/web/authentication/www/BasicAuthenticationFilterTests.java
index 006e5f7b79..c13422e8a3 100644
--- a/web/src/test/java/org/springframework/security/web/authentication/www/BasicAuthenticationFilterTests.java
+++ b/web/src/test/java/org/springframework/security/web/authentication/www/BasicAuthenticationFilterTests.java
@@ -36,6 +36,8 @@ import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.mock.web.MockHttpSession;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
+import org.springframework.security.authentication.NonBuildableAuthenticationToken;
+import org.springframework.security.authentication.SecurityAssertions;
import org.springframework.security.authentication.TestingAuthenticationToken;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
@@ -539,6 +541,25 @@ public class BasicAuthenticationFilterTests {
.containsExactly(DefaultEqualsGrantedAuthority.AUTHORITY);
}
+ @Test
+ void doFilterWhenNotOverridingToBuilderThenDoesNotMergeAuthorities() throws Exception {
+ TestingAuthenticationToken existingAuthn = new TestingAuthenticationToken("username", "password", "FACTORONE");
+ SecurityContextHolder.setContext(new SecurityContextImpl(existingAuthn));
+ MockHttpServletRequest request = new MockHttpServletRequest();
+ request.addHeader(HttpHeaders.AUTHORIZATION, "Basic " + CodecTestUtils.encodeBase64("a:b"));
+ MockHttpServletResponse response = new MockHttpServletResponse();
+ AuthenticationManager manager = mock(AuthenticationManager.class);
+ given(manager.authenticate(any()))
+ .willReturn(new NonBuildableAuthenticationToken("username", "password", "FACTORTWO"));
+ BasicAuthenticationFilter filter = new BasicAuthenticationFilter(manager);
+ filter.doFilter(request, response, new MockFilterChain());
+ Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
+ SecurityAssertions.assertThat(authentication)
+ .authorities()
+ .extracting(GrantedAuthority::getAuthority)
+ .containsExactly("FACTORTWO");
+ }
+
@Test
public void doFilterWhenCustomAuthenticationConverterRequestThenAuthenticate() throws Exception {
this.filter.setAuthenticationConverter(new TestAuthenticationConverter());
diff --git a/web/src/test/java/org/springframework/security/web/server/authentication/AuthenticationWebFilterTests.java b/web/src/test/java/org/springframework/security/web/server/authentication/AuthenticationWebFilterTests.java
index a9c8c35b5a..47d3b04f3c 100644
--- a/web/src/test/java/org/springframework/security/web/server/authentication/AuthenticationWebFilterTests.java
+++ b/web/src/test/java/org/springframework/security/web/server/authentication/AuthenticationWebFilterTests.java
@@ -25,8 +25,10 @@ import org.mockito.junit.jupiter.MockitoExtension;
import reactor.core.publisher.Mono;
import org.springframework.security.authentication.BadCredentialsException;
+import org.springframework.security.authentication.NonBuildableAuthenticationToken;
import org.springframework.security.authentication.ReactiveAuthenticationManager;
import org.springframework.security.authentication.ReactiveAuthenticationManagerResolver;
+import org.springframework.security.authentication.SecurityAssertions;
import org.springframework.security.authentication.TestingAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
@@ -230,6 +232,31 @@ public class AuthenticationWebFilterTests {
.containsExactly(DefaultEqualsGrantedAuthority.AUTHORITY);
}
+ @Test
+ void doFilterWhenNotOverridingToBuilderThenDoesNotMergeAuthorities() throws Exception {
+ TestingAuthenticationToken existingAuthn = new TestingAuthenticationToken("username", "password", "FACTORONE");
+ given(this.authenticationManager.authenticate(any()))
+ .willReturn(Mono.just(new NonBuildableAuthenticationToken("user", "password", "FACTORTWO")));
+ given(this.securityContextRepository.save(any(), any())).willReturn(Mono.empty());
+ this.filter = new AuthenticationWebFilter(this.authenticationManager);
+ this.filter.setSecurityContextRepository(this.securityContextRepository);
+ WebTestClient client = WebTestClientBuilder.bindToWebFilters(new RunAsWebFilter(existingAuthn), this.filter)
+ .build();
+ client.get()
+ .uri("/")
+ .headers((headers) -> headers.setBasicAuth("test", "this"))
+ .exchange()
+ .expectStatus()
+ .isOk();
+ ArgumentCaptor context = ArgumentCaptor.forClass(SecurityContext.class);
+ verify(this.securityContextRepository).save(any(), context.capture());
+ Authentication authentication = context.getValue().getAuthentication();
+ SecurityAssertions.assertThat(authentication)
+ .authorities()
+ .extracting(GrantedAuthority::getAuthority)
+ .containsExactly("FACTORTWO");
+ }
+
@Test
public void filterWhenAuthenticationManagerResolverDefaultsAndAuthenticationFailThenUnauthorized() {
given(this.authenticationManager.authenticate(any()))