Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,7 @@
* @author Ankur Pathak
* @author Alexey Nesterov
* @author Yanming Zhou
* @author Iain Henderson
* @since 5.0
*/
public class ServerHttpSecurity {
Expand Down Expand Up @@ -4138,6 +4139,8 @@ public class OAuth2ResourceServerSpec {

private ServerAuthenticationFailureHandler authenticationFailureHandler;

private ServerAuthenticationSuccessHandler authenticationSuccessHandler;

private ServerAccessDeniedHandler accessDeniedHandler = new BearerTokenServerAccessDeniedHandler();

private ServerAuthenticationConverter bearerTokenConverter = new ServerBearerTokenAuthenticationConverter();
Expand Down Expand Up @@ -4186,6 +4189,20 @@ public OAuth2ResourceServerSpec authenticationFailureHandler(
return this;
}

/**
* Configures the {@link ServerAuthenticationSuccessHandler} to use. The default
* is {@link WebFilterChainServerAuthenticationSuccessHandler}
* @param authenticationSuccessHandler the
* {@link ServerAuthenticationSuccessHandler} to use
* @return the {@link OAuth2ClientSpec} to customize
* @since 7.1
*/
public OAuth2ResourceServerSpec authenticationSuccessHandler(
ServerAuthenticationSuccessHandler authenticationSuccessHandler) {
this.authenticationSuccessHandler = authenticationSuccessHandler;
return this;
}

/**
* Configures the {@link ServerAuthenticationConverter} to use for requests
* authenticating with
Expand Down Expand Up @@ -4254,6 +4271,7 @@ protected void configure(ServerHttpSecurity http) {
AuthenticationWebFilter oauth2 = new AuthenticationWebFilter(this.authenticationManagerResolver);
oauth2.setServerAuthenticationConverter(this.bearerTokenConverter);
oauth2.setAuthenticationFailureHandler(authenticationFailureHandler());
oauth2.setAuthenticationSuccessHandler(authenticationSuccessHandler());
http.addFilterAt(oauth2, SecurityWebFiltersOrder.AUTHENTICATION);
}
else if (this.jwt != null) {
Expand Down Expand Up @@ -4313,6 +4331,13 @@ private ServerAuthenticationFailureHandler authenticationFailureHandler() {
return new ServerAuthenticationEntryPointFailureHandler(this.entryPoint);
}

private ServerAuthenticationSuccessHandler authenticationSuccessHandler() {
if (this.authenticationSuccessHandler != null) {
return this.authenticationSuccessHandler;
}
return new WebFilterChainServerAuthenticationSuccessHandler();
}

/**
* Configures JWT Resource Server Support
*/
Expand Down Expand Up @@ -4387,6 +4412,7 @@ protected void configure(ServerHttpSecurity http) {
AuthenticationWebFilter oauth2 = new AuthenticationWebFilter(authenticationManager);
oauth2.setServerAuthenticationConverter(OAuth2ResourceServerSpec.this.bearerTokenConverter);
oauth2.setAuthenticationFailureHandler(authenticationFailureHandler());
oauth2.setAuthenticationSuccessHandler(authenticationSuccessHandler());
http.addFilterAt(oauth2, SecurityWebFiltersOrder.AUTHENTICATION);
}

Expand Down Expand Up @@ -4519,6 +4545,7 @@ protected void configure(ServerHttpSecurity http) {
AuthenticationWebFilter oauth2 = new AuthenticationWebFilter(authenticationManager);
oauth2.setServerAuthenticationConverter(OAuth2ResourceServerSpec.this.bearerTokenConverter);
oauth2.setAuthenticationFailureHandler(authenticationFailureHandler());
oauth2.setAuthenticationSuccessHandler(authenticationSuccessHandler());
http.addFilterAt(oauth2, SecurityWebFiltersOrder.AUTHENTICATION);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,9 +73,11 @@
import org.springframework.security.oauth2.server.resource.authentication.ReactiveJwtAuthenticationConverterAdapter;
import org.springframework.security.oauth2.server.resource.introspection.ReactiveOpaqueTokenAuthenticationConverter;
import org.springframework.security.web.server.SecurityWebFilterChain;
import org.springframework.security.web.server.WebFilterExchange;
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.authentication.ServerAuthenticationSuccessHandler;
import org.springframework.security.web.server.authorization.HttpStatusServerAccessDeniedHandler;
import org.springframework.test.web.reactive.server.WebTestClient;
import org.springframework.web.bind.annotation.GetMapping;
Expand Down Expand Up @@ -371,6 +373,78 @@ public void getWhenUsingCustomAuthenticationFailureHandlerThenUsesIsAccordingly(
verify(handler).onAuthenticationFailure(any(), any());
}

@Test
public void getWhenUsingCustomAuthenticationSuccessHandlerThenUsesIsAccordingly() {
this.spring.register(CustomAuthenticationSuccessHandlerAuthenticationManagerResolverConfig.class).autowire();
ServerAuthenticationSuccessHandler handler = this.spring.getContext()
.getBean(ServerAuthenticationSuccessHandler.class);
ReactiveAuthenticationManager authenticationManager = this.spring.getContext()
.getBean(ReactiveAuthenticationManager.class);
given(authenticationManager.authenticate(any()))
.willAnswer(input -> Mono.just(input.getArgument(0, Authentication.class)));
given(handler.onAuthenticationSuccess(any(), any())).willAnswer(input -> {
WebFilterExchange webFilterExchange = input.getArgument(0, WebFilterExchange.class);
return webFilterExchange.getChain().filter(webFilterExchange.getExchange());
});
// @formatter:off
this.client.get()
.headers((headers) -> headers.setBearerAuth(this.messageReadToken))
.exchange()
.expectStatus().isUnauthorized();
// @formatter:on
verify(handler).onAuthenticationSuccess(any(), any());
}

@Test
public void getWhenUsingCustomAuthenticationSuccessHandlerWithJwtThenUsesIsAccordingly() {
this.spring.register(CustomAuthenticationSuccessHandlerJwtConfig.class).autowire();
ServerAuthenticationSuccessHandler handler = this.spring.getContext()
.getBean(ServerAuthenticationSuccessHandler.class);
ReactiveAuthenticationManager authenticationManager = this.spring.getContext()
.getBean(ReactiveAuthenticationManager.class);
given(authenticationManager.authenticate(any()))
.willAnswer(input -> Mono.just(input.getArgument(0, Authentication.class)));
given(handler.onAuthenticationSuccess(any(), any())).willAnswer(input -> {
WebFilterExchange webFilterExchange = input.getArgument(0, WebFilterExchange.class);
return webFilterExchange.getChain().filter(webFilterExchange.getExchange());
});
// @formatter:off
this.client.get()
.headers((headers) -> headers.setBearerAuth(this.messageReadToken))
.exchange()
.expectStatus().isUnauthorized();
// @formatter:on
verify(handler).onAuthenticationSuccess(any(), any());
}

@Test
public void getWhenUsingCustomAuthenticationSuccessHandlerWIthOpaqueTokenThenUsesIsAccordingly() {
this.spring.register(CustomAuthenticationSuccessHandlerOpaqueTokenConfig.class, RootController.class).autowire();
this.spring.getContext()
.getBean(MockWebServer.class)
.setDispatcher(requiresAuth(this.clientId, this.clientSecret, this.active));
ServerAuthenticationSuccessHandler handler = this.spring.getContext()
.getBean(ServerAuthenticationSuccessHandler.class);
ReactiveAuthenticationManager authenticationManager = this.spring.getContext()
.getBean(ReactiveAuthenticationManager.class);
given(authenticationManager.authenticate(any()))
.willAnswer(input -> Mono.just(input.getArgument(0, Authentication.class)));
given(handler.onAuthenticationSuccess(any(), any())).willAnswer(input -> {
WebFilterExchange webFilterExchange = input.getArgument(0, WebFilterExchange.class);
return webFilterExchange.getChain().filter(webFilterExchange.getExchange());
});
// @formatter:off
this.client.get()
.headers((headers) -> headers
.setBearerAuth(this.messageReadToken)
)
.exchange()
.expectStatus().isOk();
// @formatter:on

verify(handler).onAuthenticationSuccess(any(), any());
}

@Test
public void postWhenSignedThenReturnsOk() {
this.spring.register(PublicKeyConfig.class, RootController.class).autowire();
Expand Down Expand Up @@ -950,6 +1024,110 @@ ServerAuthenticationFailureHandler authenticationFailureHandler() {

}

@Configuration
@EnableWebFlux
@EnableWebFluxSecurity
static class CustomAuthenticationSuccessHandlerAuthenticationManagerResolverConfig {

@Bean
SecurityWebFilterChain springSecurity(ServerHttpSecurity http) {
// @formatter:off
http
.authorizeExchange((authorize) -> authorize.anyExchange().authenticated())
.oauth2ResourceServer((oauth2) -> oauth2
.authenticationSuccessHandler(authenticationSuccessHandler())
.authenticationManagerResolver(exchange -> Mono.just(authenticationManager()))
);
// @formatter:on
return http.build();
}

@Bean
ReactiveAuthenticationManager authenticationManager() {
return mock(ReactiveAuthenticationManager.class);
}

@Bean
ServerAuthenticationSuccessHandler authenticationSuccessHandler() {
return mock(ServerAuthenticationSuccessHandler.class);
}

}

@Configuration
@EnableWebFlux
@EnableWebFluxSecurity
static class CustomAuthenticationSuccessHandlerJwtConfig {

@Bean
SecurityWebFilterChain springSecurity(ServerHttpSecurity http) {
// @formatter:off
http
.authorizeExchange((authorize) -> authorize.anyExchange().authenticated())
.oauth2ResourceServer((oauth2) -> oauth2
.authenticationSuccessHandler(authenticationSuccessHandler())
.jwt((jwt) -> jwt.authenticationManager(authenticationManager()))
);
// @formatter:on
return http.build();
}

@Bean
ReactiveAuthenticationManager authenticationManager() {
return mock(ReactiveAuthenticationManager.class);
}

@Bean
ServerAuthenticationSuccessHandler authenticationSuccessHandler() {
return mock(ServerAuthenticationSuccessHandler.class);
}

}

@Configuration
@EnableWebFlux
@EnableWebFluxSecurity
static class CustomAuthenticationSuccessHandlerOpaqueTokenConfig {

private MockWebServer mockWebServer = new MockWebServer();

@Bean
SecurityWebFilterChain springSecurity(ServerHttpSecurity http) {
String introspectionUri = mockWebServer().url("/introspect").toString();
// @formatter:off
http
.authorizeExchange((authorize) -> authorize.anyExchange().authenticated())
.oauth2ResourceServer((oauth2) -> oauth2
.authenticationSuccessHandler(authenticationSuccessHandler())
.opaqueToken((opaqueToken) -> opaqueToken
.introspectionUri(introspectionUri)
.introspectionClientCredentials("client", "secret"))
);
// @formatter:on
return http.build();
}

@Bean
ReactiveAuthenticationManager authenticationManager() {
return mock(ReactiveAuthenticationManager.class);
}

@Bean
ServerAuthenticationSuccessHandler authenticationSuccessHandler() {
return mock(ServerAuthenticationSuccessHandler.class);
}

@Bean
MockWebServer mockWebServer() {
return this.mockWebServer;
}

@PreDestroy
void shutdown() throws IOException {
this.mockWebServer.shutdown();
}
}

@EnableWebFlux
@EnableWebFluxSecurity
static class CustomBearerTokenServerAuthenticationConverter {
Expand Down