diff --git a/web/src/main/java/org/springframework/security/web/server/authentication/DelegatingServerAuthenticationSuccessHandler.java b/web/src/main/java/org/springframework/security/web/server/authentication/DelegatingServerAuthenticationSuccessHandler.java index 2fafd24bec..019442b5af 100644 --- a/web/src/main/java/org/springframework/security/web/server/authentication/DelegatingServerAuthenticationSuccessHandler.java +++ b/web/src/main/java/org/springframework/security/web/server/authentication/DelegatingServerAuthenticationSuccessHandler.java @@ -19,12 +19,11 @@ package org.springframework.security.web.server.authentication; import org.springframework.security.core.Authentication; import org.springframework.security.web.server.WebFilterExchange; import org.springframework.util.Assert; +import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import java.util.Arrays; import java.util.List; -import java.util.stream.Collectors; -import java.util.stream.Stream; /** * Delegates to a collection of {@link ServerAuthenticationSuccessHandler} implementations. @@ -43,7 +42,8 @@ public class DelegatingServerAuthenticationSuccessHandler implements ServerAuthe @Override public Mono onAuthenticationSuccess(WebFilterExchange exchange, Authentication authentication) { - Stream> results = this.delegates.stream().map(delegate -> delegate.onAuthenticationSuccess(exchange, authentication)); - return Mono.when(results.collect(Collectors.toList())); + return Flux.fromIterable(this.delegates) + .concatMap(delegate -> delegate.onAuthenticationSuccess(exchange, authentication)) + .then(); } } diff --git a/web/src/test/groovy/org/springframework/security/web/server/authentication/DelegatingServerAuthenticationSuccessHandlerTests.java b/web/src/test/groovy/org/springframework/security/web/server/authentication/DelegatingServerAuthenticationSuccessHandlerTests.java index 9bf67714b3..1d7037940c 100644 --- a/web/src/test/groovy/org/springframework/security/web/server/authentication/DelegatingServerAuthenticationSuccessHandlerTests.java +++ b/web/src/test/groovy/org/springframework/security/web/server/authentication/DelegatingServerAuthenticationSuccessHandlerTests.java @@ -16,10 +16,6 @@ package org.springframework.security.web.server.authentication; -import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.when; - import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -27,9 +23,19 @@ import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; import org.springframework.security.core.Authentication; import org.springframework.security.web.server.WebFilterExchange; - +import reactor.core.publisher.Mono; import reactor.test.publisher.PublisherProbe; +import java.time.Duration; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.when; + /** * @author Rob Winch * @since 5.1 @@ -88,4 +94,26 @@ public class DelegatingServerAuthenticationSuccessHandlerTests { this.delegate1Result.assertWasSubscribed(); this.delegate2Result.assertWasSubscribed(); } + + @Test + public void onAuthenticationSuccessSequential() throws Exception { + AtomicBoolean slowDone = new AtomicBoolean(); + CountDownLatch latch = new CountDownLatch(1); + ServerAuthenticationSuccessHandler slow = (exchange, authentication) -> + Mono.delay(Duration.ofMillis(100)) + .doOnSuccess(__ -> slowDone.set(true)) + .then(); + ServerAuthenticationSuccessHandler second = (exchange, authentication) -> + Mono.fromRunnable(() -> { + latch.countDown(); + assertThat(slowDone.get()) + .describedAs("ServerAuthenticationSuccessHandler should be executed sequentially") + .isTrue(); + }); + DelegatingServerAuthenticationSuccessHandler handler = new DelegatingServerAuthenticationSuccessHandler(slow, second); + + handler.onAuthenticationSuccess(this.exchange, this.authentication).block(); + + assertThat(latch.await(3, TimeUnit.SECONDS)).isTrue(); + } }