Add authenticationFailureHandler method in OAuth2LoginSpec
Allow to customize the failure handler. Fixes gh-7051
This commit is contained in:
parent
b3da1e466b
commit
2c836a171a
|
@ -54,7 +54,6 @@ import org.springframework.security.authorization.AuthorityReactiveAuthorization
|
|||
import org.springframework.security.authorization.AuthorizationDecision;
|
||||
import org.springframework.security.authorization.ReactiveAuthorizationManager;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.authority.AuthorityUtils;
|
||||
import org.springframework.security.core.userdetails.ReactiveUserDetailsService;
|
||||
|
@ -103,7 +102,6 @@ import org.springframework.security.web.server.DelegatingServerAuthenticationEnt
|
|||
import org.springframework.security.web.server.MatcherSecurityWebFilterChain;
|
||||
import org.springframework.security.web.server.SecurityWebFilterChain;
|
||||
import org.springframework.security.web.server.ServerAuthenticationEntryPoint;
|
||||
import org.springframework.security.web.server.WebFilterExchange;
|
||||
import org.springframework.security.web.server.authentication.AnonymousAuthenticationWebFilter;
|
||||
import org.springframework.security.web.server.authentication.AuthenticationWebFilter;
|
||||
import org.springframework.security.web.server.authentication.HttpBasicServerAuthenticationEntryPoint;
|
||||
|
@ -233,6 +231,7 @@ import static org.springframework.security.web.server.util.matcher.ServerWebExch
|
|||
* @author Rob Winch
|
||||
* @author Vedran Pavic
|
||||
* @author Rafiullah Hamedy
|
||||
* @author Eddú Meléndez
|
||||
* @since 5.0
|
||||
*/
|
||||
public class ServerHttpSecurity {
|
||||
|
@ -981,6 +980,8 @@ public class ServerHttpSecurity {
|
|||
|
||||
private ServerAuthenticationSuccessHandler authenticationSuccessHandler = new RedirectServerAuthenticationSuccessHandler();
|
||||
|
||||
private ServerAuthenticationFailureHandler authenticationFailureHandler = (webFilterExchange, exception) -> Mono.error(exception);
|
||||
|
||||
/**
|
||||
* Configures the {@link ReactiveAuthenticationManager} to use. The default is
|
||||
* {@link OAuth2AuthorizationCodeReactiveAuthenticationManager}
|
||||
|
@ -1006,6 +1007,19 @@ public class ServerHttpSecurity {
|
|||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* The {@link ServerAuthenticationFailureHandler} used after authentication failure.
|
||||
*
|
||||
* @since 5.2
|
||||
* @param authenticationFailureHandler the failure handler to use
|
||||
* @return the {@link OAuth2LoginSpec} to customize
|
||||
*/
|
||||
public OAuth2LoginSpec authenticationFailureHandler(ServerAuthenticationFailureHandler authenticationFailureHandler) {
|
||||
Assert.notNull(authenticationFailureHandler, "authenticationFailureHandler cannot be null");
|
||||
this.authenticationFailureHandler = authenticationFailureHandler;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the {@link ReactiveAuthenticationManager} to use. First tries an explicitly configured manager, and
|
||||
* defaults to {@link OAuth2AuthorizationCodeReactiveAuthenticationManager}
|
||||
|
@ -1123,13 +1137,7 @@ public class ServerHttpSecurity {
|
|||
authenticationFilter.setServerAuthenticationConverter(getAuthenticationConverter(clientRegistrationRepository));
|
||||
|
||||
authenticationFilter.setAuthenticationSuccessHandler(this.authenticationSuccessHandler);
|
||||
authenticationFilter.setAuthenticationFailureHandler(new ServerAuthenticationFailureHandler() {
|
||||
@Override
|
||||
public Mono<Void> onAuthenticationFailure(WebFilterExchange webFilterExchange,
|
||||
AuthenticationException exception) {
|
||||
return Mono.error(exception);
|
||||
}
|
||||
});
|
||||
authenticationFilter.setAuthenticationFailureHandler(this.authenticationFailureHandler);
|
||||
authenticationFilter.setSecurityContextRepository(new WebSessionServerSecurityContextRepository());
|
||||
|
||||
MediaTypeServerWebExchangeMatcher htmlMatcher = new MediaTypeServerWebExchangeMatcher(
|
||||
|
|
|
@ -25,8 +25,14 @@ import org.junit.Rule;
|
|||
import org.junit.Test;
|
||||
import org.mockito.stubbing.Answer;
|
||||
import org.openqa.selenium.WebDriver;
|
||||
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
|
||||
import org.springframework.security.oauth2.core.OAuth2Error;
|
||||
import org.springframework.security.web.server.WebFilterExchange;
|
||||
import org.springframework.security.web.server.authentication.RedirectServerAuthenticationFailureHandler;
|
||||
import org.springframework.security.web.server.authentication.RedirectServerAuthenticationSuccessHandler;
|
||||
import org.springframework.security.web.server.authentication.ServerAuthenticationFailureHandler;
|
||||
import org.springframework.security.web.server.authentication.ServerAuthenticationSuccessHandler;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
|
@ -97,6 +103,7 @@ import static org.mockito.Mockito.when;
|
|||
|
||||
/**
|
||||
* @author Rob Winch
|
||||
* @author Eddú Meléndez
|
||||
* @since 5.1
|
||||
*/
|
||||
public class OAuth2LoginTests {
|
||||
|
@ -233,6 +240,59 @@ public class OAuth2LoginTests {
|
|||
verify(successHandler).onAuthenticationSuccess(any(), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void oauth2LoginFailsWhenCustomObjectsThenUsed() {
|
||||
this.spring.register(OAuth2LoginWithSingleClientRegistrations.class,
|
||||
OAuth2LoginMockAuthenticationManagerConfig.class).autowire();
|
||||
|
||||
String redirectLocation = "/custom-redirect-location";
|
||||
String failureRedirectLocation = "/failure-redirect-location";
|
||||
|
||||
WebTestClient webTestClient = WebTestClientBuilder
|
||||
.bindToWebFilters(this.springSecurity)
|
||||
.build();
|
||||
|
||||
OAuth2LoginMockAuthenticationManagerConfig config = this.spring.getContext()
|
||||
.getBean(OAuth2LoginMockAuthenticationManagerConfig.class);
|
||||
ServerAuthenticationConverter converter = config.authenticationConverter;
|
||||
ReactiveAuthenticationManager manager = config.manager;
|
||||
ServerWebExchangeMatcher matcher = config.matcher;
|
||||
ServerOAuth2AuthorizationRequestResolver resolver = config.resolver;
|
||||
ServerAuthenticationSuccessHandler successHandler = config.successHandler;
|
||||
ServerAuthenticationFailureHandler failureHandler = config.failureHandler;
|
||||
|
||||
when(converter.convert(any())).thenReturn(Mono.just(new TestingAuthenticationToken("a", "b", "c")));
|
||||
when(manager.authenticate(any())).thenReturn(Mono.error(new OAuth2AuthenticationException(new OAuth2Error("error"), "message")));
|
||||
when(matcher.matches(any())).thenReturn(ServerWebExchangeMatcher.MatchResult.match());
|
||||
when(resolver.resolve(any())).thenReturn(Mono.empty());
|
||||
when(successHandler.onAuthenticationSuccess(any(), any())).thenAnswer((Answer<Mono<Void>>) invocation -> {
|
||||
WebFilterExchange webFilterExchange = invocation.getArgument(0);
|
||||
Authentication authentication = invocation.getArgument(1);
|
||||
|
||||
return new RedirectServerAuthenticationSuccessHandler(redirectLocation)
|
||||
.onAuthenticationSuccess(webFilterExchange, authentication);
|
||||
});
|
||||
when(failureHandler.onAuthenticationFailure(any(), any())).thenAnswer((Answer<Mono<Void>>) invocation -> {
|
||||
WebFilterExchange webFilterExchange = invocation.getArgument(0);
|
||||
AuthenticationException authenticationException = invocation.getArgument(1);
|
||||
|
||||
return new RedirectServerAuthenticationFailureHandler(failureRedirectLocation)
|
||||
.onAuthenticationFailure(webFilterExchange, authenticationException);
|
||||
});
|
||||
|
||||
webTestClient.get()
|
||||
.uri("/login/oauth2/code/github")
|
||||
.exchange()
|
||||
.expectStatus().is3xxRedirection()
|
||||
.expectHeader().valueEquals("Location", failureRedirectLocation);
|
||||
|
||||
verify(converter).convert(any());
|
||||
verify(manager).authenticate(any());
|
||||
verify(matcher).matches(any());
|
||||
verify(resolver).resolve(any());
|
||||
verify(failureHandler).onAuthenticationFailure(any(), any());
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class OAuth2LoginMockAuthenticationManagerConfig {
|
||||
ReactiveAuthenticationManager manager = mock(ReactiveAuthenticationManager.class);
|
||||
|
@ -245,6 +305,8 @@ public class OAuth2LoginTests {
|
|||
|
||||
ServerAuthenticationSuccessHandler successHandler = mock(ServerAuthenticationSuccessHandler.class);
|
||||
|
||||
ServerAuthenticationFailureHandler failureHandler = mock(ServerAuthenticationFailureHandler.class);
|
||||
|
||||
@Bean
|
||||
public SecurityWebFilterChain springSecurityFilter(ServerHttpSecurity http) {
|
||||
http
|
||||
|
@ -256,7 +318,8 @@ public class OAuth2LoginTests {
|
|||
.authenticationManager(manager)
|
||||
.authenticationMatcher(matcher)
|
||||
.authorizationRequestResolver(resolver)
|
||||
.authenticationSuccessHandler(successHandler);
|
||||
.authenticationSuccessHandler(successHandler)
|
||||
.authenticationFailureHandler(failureHandler);
|
||||
return http.build();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2018 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.
|
||||
|
@ -18,6 +18,7 @@ package org.springframework.security.oauth2.core.endpoint;
|
|||
|
||||
/**
|
||||
* @author Rob Winch
|
||||
* @author Eddú Meléndez
|
||||
* @since 5.1
|
||||
*/
|
||||
public class TestOAuth2AuthorizationExchanges {
|
||||
|
@ -27,4 +28,10 @@ public class TestOAuth2AuthorizationExchanges {
|
|||
OAuth2AuthorizationResponse response = TestOAuth2AuthorizationResponses.success().build();
|
||||
return new OAuth2AuthorizationExchange(request, response);
|
||||
}
|
||||
|
||||
public static OAuth2AuthorizationExchange failure() {
|
||||
OAuth2AuthorizationRequest request = TestOAuth2AuthorizationRequests.request().build();
|
||||
OAuth2AuthorizationResponse response = TestOAuth2AuthorizationResponses.error().build();
|
||||
return new OAuth2AuthorizationExchange(request, response);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue