Browse Source

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
Tadaya Tsuyukubo 6 years ago
parent
commit
7782e29a58

+ 6 - 2
config/src/main/java/org/springframework/security/config/web/server/ServerHttpSecurity.java

@@ -1667,11 +1667,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);
 			}

+ 43 - 1
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;

+ 21 - 1
config/src/test/java/org/springframework/security/config/web/server/ServerHttpSecurityTests.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.
@@ -20,6 +20,7 @@ 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;
@@ -282,6 +283,25 @@ public class ServerHttpSecurityTests {
 		assertThat(result.getResponseCookies().getFirst("SESSION")).isNull();
 	}
 
+	@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);
+	}
+
 	@Test
 	@SuppressWarnings("unchecked")
 	public void addsX509FilterWhenX509AuthenticationIsConfigured() {