From 9d543ce4d13d2a4d1657f9d108e0f4e79d52aed8 Mon Sep 17 00:00:00 2001 From: Rob Winch Date: Fri, 28 Jun 2019 11:21:18 -0500 Subject: [PATCH] Allow custom ReactiveAuthenticationManager for basic and form auth Prior to this change, "HttpBasicSpec#authenticationManager" and "FormLoginSpec#authenticationManager" were always overridden by "ServerHttpSecurity#authenticationManager". This commit makes sure override only happens when custom authentication manager was not specified. Fixes: gh-5660 --- .../config/web/server/ServerHttpSecurity.java | 8 +++- .../config/web/server/FormLoginTests.java | 44 ++++++++++++++++++- .../web/server/ServerHttpSecurityTests.java | 21 +++++++++ 3 files changed, 70 insertions(+), 3 deletions(-) diff --git a/config/src/main/java/org/springframework/security/config/web/server/ServerHttpSecurity.java b/config/src/main/java/org/springframework/security/config/web/server/ServerHttpSecurity.java index ad194406e2..3ba057b5b0 100644 --- a/config/src/main/java/org/springframework/security/config/web/server/ServerHttpSecurity.java +++ b/config/src/main/java/org/springframework/security/config/web/server/ServerHttpSecurity.java @@ -1277,11 +1277,15 @@ public class ServerHttpSecurity { this.cors.configure(this); } if (this.httpBasic != null) { - this.httpBasic.authenticationManager(this.authenticationManager); + if (this.httpBasic.authenticationManager == null) { + this.httpBasic.authenticationManager(this.authenticationManager); + } this.httpBasic.configure(this); } if (this.formLogin != null) { - this.formLogin.authenticationManager(this.authenticationManager); + if (this.formLogin.authenticationManager == null) { + this.formLogin.authenticationManager(this.authenticationManager); + } if (this.securityContextRepository != null) { this.formLogin.securityContextRepository(this.securityContextRepository); } diff --git a/config/src/test/java/org/springframework/security/config/web/server/FormLoginTests.java b/config/src/test/java/org/springframework/security/config/web/server/FormLoginTests.java index cbf1a2782b..96a174f3b6 100644 --- a/config/src/test/java/org/springframework/security/config/web/server/FormLoginTests.java +++ b/config/src/test/java/org/springframework/security/config/web/server/FormLoginTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original author or authors. + * Copyright 2002-2019 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. @@ -23,6 +23,8 @@ import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; import org.openqa.selenium.support.FindBy; import org.openqa.selenium.support.PageFactory; +import org.springframework.security.authentication.ReactiveAuthenticationManager; +import org.springframework.security.authentication.TestingAuthenticationToken; import org.springframework.security.config.annotation.web.reactive.ServerHttpSecurityConfigurationBuilder; import org.springframework.security.htmlunit.server.WebTestClientHtmlUnitDriverBuilder; import org.springframework.security.test.web.reactive.server.WebTestClientBuilder; @@ -40,6 +42,10 @@ import reactor.core.publisher.Mono; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatCode; import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verifyZeroInteractions; /** * @author Rob Winch @@ -152,6 +158,42 @@ public class FormLoginTests { assertThat(driver.getCurrentUrl()).endsWith("/custom"); } + @Test + public void customAuthenticationManager() { + ReactiveAuthenticationManager defaultAuthenticationManager = mock(ReactiveAuthenticationManager.class); + ReactiveAuthenticationManager customAuthenticationManager = mock(ReactiveAuthenticationManager.class); + + given(defaultAuthenticationManager.authenticate(any())).willThrow(new RuntimeException("should not interact with default auth manager")); + given(customAuthenticationManager.authenticate(any())).willReturn(Mono.just(new TestingAuthenticationToken("user", "password", "ROLE_USER", "ROLE_ADMIN"))); + + SecurityWebFilterChain securityWebFilter = this.http + .authenticationManager(defaultAuthenticationManager) + .formLogin() + .authenticationManager(customAuthenticationManager) + .and() + .build(); + + WebTestClient webTestClient = WebTestClientBuilder + .bindToWebFilters(securityWebFilter) + .build(); + + WebDriver driver = WebTestClientHtmlUnitDriverBuilder + .webTestClientSetup(webTestClient) + .build(); + + DefaultLoginPage loginPage = DefaultLoginPage.to(driver) + .assertAt(); + + HomePage homePage = loginPage.loginForm() + .username("user") + .password("password") + .submit(HomePage.class); + + homePage.assertAt(); + + verifyZeroInteractions(defaultAuthenticationManager); + } + public static class CustomLoginPage { private WebDriver driver; diff --git a/config/src/test/java/org/springframework/security/config/web/server/ServerHttpSecurityTests.java b/config/src/test/java/org/springframework/security/config/web/server/ServerHttpSecurityTests.java index 58516f14ba..ff538356b2 100644 --- a/config/src/test/java/org/springframework/security/config/web/server/ServerHttpSecurityTests.java +++ b/config/src/test/java/org/springframework/security/config/web/server/ServerHttpSecurityTests.java @@ -19,6 +19,8 @@ package org.springframework.security.config.web.server; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.BDDMockito.given; import static org.mockito.Matchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verifyZeroInteractions; import static org.mockito.Mockito.when; import java.util.Arrays; @@ -190,6 +192,25 @@ public class ServerHttpSecurityTests { .isEqualTo(Arrays.asList(SecurityContextServerLogoutHandler.class, CsrfServerLogoutHandler.class)); } + @Test + public void basicWithCustomAuthenticationManager() { + ReactiveAuthenticationManager customAuthenticationManager = mock(ReactiveAuthenticationManager.class); + given(customAuthenticationManager.authenticate(any())).willReturn(Mono.just(new TestingAuthenticationToken("rob", "rob", "ROLE_USER", "ROLE_ADMIN"))); + + SecurityWebFilterChain securityFilterChain = this.http.httpBasic().authenticationManager(customAuthenticationManager).and().build(); + WebFilterChainProxy springSecurityFilterChain = new WebFilterChainProxy(securityFilterChain); + WebTestClient client = WebTestClientBuilder.bindToWebFilters(springSecurityFilterChain).build(); + + client.get() + .uri("/") + .headers(headers -> headers.setBasicAuth("rob", "rob")) + .exchange() + .expectStatus().isOk() + .expectBody(String.class).consumeWith(b -> assertThat(b.getResponseBody()).isEqualTo("ok")); + + verifyZeroInteractions(this.authenticationManager); + } + private Optional getWebFilter(SecurityWebFilterChain filterChain, Class filterClass) { return (Optional) filterChain.getWebFilters() .filter(Objects::nonNull)