Merge branch '5.8.x'

Closes gh-12133
This commit is contained in:
Josh Cummings
2022-11-02 15:49:18 -06:00
7 changed files with 199 additions and 13 deletions

View File

@@ -51,6 +51,7 @@ import org.springframework.core.convert.converter.Converter;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.ReactiveAuthenticationManager;
import org.springframework.security.authentication.ReactiveAuthenticationManagerResolver;
import org.springframework.security.authentication.TestingAuthenticationToken;
@@ -73,6 +74,7 @@ import org.springframework.security.oauth2.server.resource.introspection.Reactiv
import org.springframework.security.web.server.SecurityWebFilterChain;
import org.springframework.security.web.server.authentication.HttpStatusServerEntryPoint;
import org.springframework.security.web.server.authentication.ServerAuthenticationConverter;
import org.springframework.security.web.server.authentication.ServerAuthenticationFailureHandler;
import org.springframework.security.web.server.authorization.HttpStatusServerAccessDeniedHandler;
import org.springframework.test.web.reactive.server.WebTestClient;
import org.springframework.web.bind.annotation.GetMapping;
@@ -347,6 +349,25 @@ public class OAuth2ResourceServerSpecTests {
// @formatter:on
}
@Test
public void getWhenUsingCustomAuthenticationFailureHandlerThenUsesIsAccordingly() {
this.spring.register(CustomAuthenticationFailureHandlerConfig.class).autowire();
ServerAuthenticationFailureHandler handler = this.spring.getContext()
.getBean(ServerAuthenticationFailureHandler.class);
ReactiveAuthenticationManager authenticationManager = this.spring.getContext()
.getBean(ReactiveAuthenticationManager.class);
given(authenticationManager.authenticate(any()))
.willReturn(Mono.error(() -> new BadCredentialsException("bad")));
given(handler.onAuthenticationFailure(any(), any())).willReturn(Mono.empty());
// @formatter:off
this.client.get()
.headers((headers) -> headers.setBearerAuth(this.messageReadToken))
.exchange()
.expectStatus().isOk();
// @formatter:on
verify(handler).onAuthenticationFailure(any(), any());
}
@Test
public void postWhenSignedThenReturnsOk() {
this.spring.register(PublicKeyConfig.class, RootController.class).autowire();
@@ -903,6 +924,35 @@ public class OAuth2ResourceServerSpecTests {
}
@Configuration
@EnableWebFlux
@EnableWebFluxSecurity
static class CustomAuthenticationFailureHandlerConfig {
@Bean
SecurityWebFilterChain springSecurity(ServerHttpSecurity http) {
// @formatter:off
http
.authorizeExchange((authorize) -> authorize.anyExchange().authenticated())
.oauth2ResourceServer((oauth2) -> oauth2
.authenticationFailureHandler(authenticationFailureHandler())
.jwt((jwt) -> jwt.authenticationManager(authenticationManager()))
);
// @formatter:on
return http.build();
}
@Bean
ReactiveAuthenticationManager authenticationManager() {
return mock(ReactiveAuthenticationManager.class);
}
@Bean
ServerAuthenticationFailureHandler authenticationFailureHandler() {
return mock(ServerAuthenticationFailureHandler.class);
}
}
@EnableWebFlux
@EnableWebFluxSecurity
static class CustomBearerTokenServerAuthenticationConverter {

View File

@@ -35,6 +35,7 @@ import reactor.test.publisher.TestPublisher;
import org.springframework.http.HttpStatus;
import org.springframework.mock.http.server.reactive.MockServerHttpRequest;
import org.springframework.mock.web.server.MockServerWebExchange;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.ReactiveAuthenticationManager;
import org.springframework.security.authentication.TestingAuthenticationToken;
import org.springframework.security.config.annotation.web.reactive.ServerHttpSecurityConfigurationBuilder;
@@ -57,6 +58,7 @@ import org.springframework.security.web.server.WebFilterChainProxy;
import org.springframework.security.web.server.authentication.AnonymousAuthenticationWebFilterTests;
import org.springframework.security.web.server.authentication.HttpBasicServerAuthenticationEntryPoint;
import org.springframework.security.web.server.authentication.HttpStatusServerEntryPoint;
import org.springframework.security.web.server.authentication.ServerAuthenticationFailureHandler;
import org.springframework.security.web.server.authentication.ServerX509AuthenticationConverter;
import org.springframework.security.web.server.authentication.logout.DelegatingServerLogoutHandler;
import org.springframework.security.web.server.authentication.logout.LogoutWebFilter;
@@ -218,6 +220,27 @@ public class ServerHttpSecurityTests {
verify(authenticationEntryPoint).commence(any(), any());
}
@Test
public void basicWhenCustomAuthenticationFailureHandlerThenUses() {
ReactiveAuthenticationManager authenticationManager = mock(ReactiveAuthenticationManager.class);
ServerAuthenticationFailureHandler authenticationFailureHandler = mock(
ServerAuthenticationFailureHandler.class);
this.http.httpBasic().authenticationFailureHandler(authenticationFailureHandler);
this.http.httpBasic().authenticationManager(authenticationManager);
this.http.authorizeExchange().anyExchange().authenticated();
given(authenticationManager.authenticate(any()))
.willReturn(Mono.error(() -> new BadCredentialsException("bad")));
given(authenticationFailureHandler.onAuthenticationFailure(any(), any())).willReturn(Mono.empty());
WebTestClient client = buildClient();
// @formatter:off
client.get().uri("/")
.headers((headers) -> headers.setBasicAuth("user", "password"))
.exchange()
.expectStatus().isOk();
// @formatter:on
verify(authenticationFailureHandler).onAuthenticationFailure(any(), any());
}
@Test
public void buildWhenServerWebExchangeFromContextThenFound() {
SecurityWebFilterChain filter = this.http.build();

View File

@@ -19,7 +19,6 @@ package org.springframework.security.config.web.server
import io.mockk.every
import io.mockk.mockkObject
import io.mockk.verify
import java.util.Base64
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
import org.springframework.beans.factory.annotation.Autowired
@@ -38,6 +37,7 @@ import org.springframework.security.core.userdetails.User
import org.springframework.security.web.server.SecurityWebFilterChain
import org.springframework.security.web.server.ServerAuthenticationEntryPoint
import org.springframework.security.web.server.authentication.HttpStatusServerEntryPoint
import org.springframework.security.web.server.authentication.ServerAuthenticationFailureHandler
import org.springframework.security.web.server.context.ServerSecurityContextRepository
import org.springframework.security.web.server.context.WebSessionServerSecurityContextRepository
import org.springframework.test.web.reactive.server.WebTestClient
@@ -45,6 +45,7 @@ import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController
import org.springframework.web.reactive.config.EnableWebFlux
import reactor.core.publisher.Mono
import java.util.*
/**
* Tests for [ServerHttpBasicDsl]
@@ -228,6 +229,43 @@ class ServerHttpBasicDslTests {
}
}
@Test
fun `http basic when custom authentication failure handler then failure handler used`() {
this.spring.register(CustomAuthenticationFailureHandlerConfig::class.java, UserDetailsConfig::class.java).autowire()
mockkObject(CustomAuthenticationFailureHandlerConfig.FAILURE_HANDLER)
every {
CustomAuthenticationFailureHandlerConfig.FAILURE_HANDLER.onAuthenticationFailure(any(), any())
} returns Mono.empty()
this.client.get()
.uri("/")
.header("Authorization", "Basic " + Base64.getEncoder().encodeToString("user:wrong".toByteArray()))
.exchange()
verify(exactly = 1) { CustomAuthenticationFailureHandlerConfig.FAILURE_HANDLER.onAuthenticationFailure(any(), any()) }
}
@EnableWebFluxSecurity
@EnableWebFlux
open class CustomAuthenticationFailureHandlerConfig {
companion object {
val FAILURE_HANDLER: ServerAuthenticationFailureHandler = ServerAuthenticationFailureHandler { _, _ -> Mono.empty() }
}
@Bean
open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
return http {
authorizeExchange {
authorize(anyExchange, authenticated)
}
httpBasic {
authenticationFailureHandler = FAILURE_HANDLER
}
}
}
}
@Configuration
open class UserDetailsConfig {
@Bean

View File

@@ -19,10 +19,6 @@ package org.springframework.security.config.web.server
import io.mockk.every
import io.mockk.mockkObject
import io.mockk.verify
import java.math.BigInteger
import java.security.KeyFactory
import java.security.interfaces.RSAPublicKey
import java.security.spec.RSAPublicKeySpec
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
import org.springframework.beans.factory.annotation.Autowired
@@ -38,11 +34,16 @@ import org.springframework.security.oauth2.server.resource.authentication.JwtIss
import org.springframework.security.oauth2.server.resource.web.server.authentication.ServerBearerTokenAuthenticationConverter
import org.springframework.security.web.server.SecurityWebFilterChain
import org.springframework.security.web.server.authentication.HttpStatusServerEntryPoint
import org.springframework.security.web.server.authentication.ServerAuthenticationFailureHandler
import org.springframework.security.web.server.authorization.HttpStatusServerAccessDeniedHandler
import org.springframework.test.web.reactive.server.WebTestClient
import org.springframework.web.reactive.config.EnableWebFlux
import org.springframework.web.server.ServerWebExchange
import reactor.core.publisher.Mono
import java.math.BigInteger
import java.security.KeyFactory
import java.security.interfaces.RSAPublicKey
import java.security.spec.RSAPublicKeySpec
/**
* Tests for [ServerOAuth2ResourceServerDsl]
@@ -129,6 +130,47 @@ class ServerOAuth2ResourceServerDslTests {
}
}
@Test
fun `http basic when custom authentication failure handler then failure handler used`() {
this.spring.register(AuthenticationFailureHandlerConfig::class.java).autowire()
mockkObject(AuthenticationFailureHandlerConfig.FAILURE_HANDLER)
every {
AuthenticationFailureHandlerConfig.FAILURE_HANDLER.onAuthenticationFailure(any(), any())
} returns Mono.empty()
this.client.get()
.uri("/")
.header("Authorization", "Bearer token")
.exchange()
.expectStatus().isOk
verify(exactly = 1) { AuthenticationFailureHandlerConfig.FAILURE_HANDLER.onAuthenticationFailure(any(), any()) }
}
@EnableWebFluxSecurity
@EnableWebFlux
open class AuthenticationFailureHandlerConfig {
companion object {
val FAILURE_HANDLER: ServerAuthenticationFailureHandler = ServerAuthenticationFailureHandler { _, _ -> Mono.empty() }
}
@Bean
open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
return http {
authorizeExchange {
authorize(anyExchange, authenticated)
}
oauth2ResourceServer {
authenticationFailureHandler = FAILURE_HANDLER
jwt {
publicKey = publicKey()
}
}
}
}
}
@Test
fun `request when custom bearer token converter configured then custom converter used`() {
this.spring.register(BearerTokenConverterConfig::class.java).autowire()