Skip to content

Commit

Permalink
Make reactive security back off without authentication manager
Browse files Browse the repository at this point in the history
If there's no authentication manager bean or no bean from which
one can be created, Spring Security's reactive support may fail to
bootstrap due to a null authentication manager.

This commit causes the auto-configuration that enables WebFlux
security to back off in the absence of an AuthenticationManager bean
and a ReactiveUserDetailsService (from which Spring Security can
create an AuthenticationManager) bean. Other reactive security
auto-configuration that can configure things such that WebFlux security
can be bootstrapped without an AuthenticationManager has been updated
to enable WebFlux security rather than relying on another
auto-configuration class to do so.

Fixes gh-37504
  • Loading branch information
wilkinsona committed Sep 25, 2023
1 parent 1d60e42 commit ee9c745
Show file tree
Hide file tree
Showing 6 changed files with 54 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.config.web.server.ServerHttpSecurity.OAuth2ResourceServerSpec;
import org.springframework.security.oauth2.core.DelegatingOAuth2TokenValidator;
Expand All @@ -49,6 +50,7 @@
import org.springframework.security.oauth2.jwt.ReactiveJwtDecoder;
import org.springframework.security.oauth2.jwt.SupplierReactiveJwtDecoder;
import org.springframework.security.web.server.SecurityWebFilterChain;
import org.springframework.security.web.server.WebFilterChainProxy;
import org.springframework.util.CollectionUtils;

/**
Expand Down Expand Up @@ -177,6 +179,13 @@ private void customDecoder(OAuth2ResourceServerSpec server, ReactiveJwtDecoder d
server.jwt((jwt) -> jwt.jwtDecoder(decoder));
}

@Configuration(proxyBeanMethods = false)
@ConditionalOnMissingBean(WebFilterChainProxy.class)
@EnableWebFluxSecurity
static class EnableWebFluxSecurityConfiguration {

}

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,12 @@
import org.springframework.boot.autoconfigure.security.oauth2.resource.OAuth2ResourceServerProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.oauth2.server.resource.introspection.ReactiveOpaqueTokenIntrospector;
import org.springframework.security.oauth2.server.resource.introspection.SpringReactiveOpaqueTokenIntrospector;
import org.springframework.security.web.server.SecurityWebFilterChain;
import org.springframework.security.web.server.WebFilterChainProxy;

import static org.springframework.security.config.Customizer.withDefaults;

Expand Down Expand Up @@ -64,6 +66,13 @@ SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
return http.build();
}

@Configuration(proxyBeanMethods = false)
@ConditionalOnMissingBean(WebFilterChainProxy.class)
@EnableWebFluxSecurity
static class EnableWebFluxSecurityConfiguration {

}

}

}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2012-2022 the original author or authors.
* Copyright 2012-2023 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.
Expand All @@ -20,13 +20,18 @@

import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.AnyNestedCondition;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.autoconfigure.security.SecurityProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
import org.springframework.security.core.userdetails.ReactiveUserDetailsService;
import org.springframework.security.web.server.WebFilterChainProxy;
import org.springframework.web.reactive.config.WebFluxConfigurer;

Expand All @@ -49,9 +54,28 @@ public class ReactiveSecurityAutoConfiguration {
@Configuration(proxyBeanMethods = false)
@ConditionalOnMissingBean(WebFilterChainProxy.class)
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE)
@Conditional(ReactiveAuthenticationManagerCondition.class)
@EnableWebFluxSecurity
static class EnableWebFluxSecurityConfiguration {

}

static final class ReactiveAuthenticationManagerCondition extends AnyNestedCondition {

ReactiveAuthenticationManagerCondition() {
super(ConfigurationPhase.REGISTER_BEAN);
}

@ConditionalOnBean(AuthenticationManager.class)
static final class ConditionalOnAuthenticationManagerBean {

}

@ConditionalOnBean(ReactiveUserDetailsService.class)
static final class ConditionalOnReactiveUserDetailsService {

}

}

}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2012-2022 the original author or authors.
* Copyright 2012-2023 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.
Expand Down Expand Up @@ -55,7 +55,7 @@
* @author Madhura Bhave
* @since 2.0.0
*/
@AutoConfiguration(after = RSocketMessagingAutoConfiguration.class)
@AutoConfiguration(before = ReactiveSecurityAutoConfiguration.class, after = RSocketMessagingAutoConfiguration.class)
@ConditionalOnClass({ ReactiveAuthenticationManager.class })
@ConditionalOnMissingBean(
value = { ReactiveAuthenticationManager.class, ReactiveUserDetailsService.class,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -740,7 +740,6 @@ private Consumer<OAuth2TokenValidator<Jwt>> audClaimValidator() {
.isEqualTo("aud");
}

@EnableWebFluxSecurity
static class TestConfig {

@Bean
Expand Down Expand Up @@ -782,6 +781,7 @@ ReactiveOpaqueTokenIntrospector decoder() {

}

@EnableWebFluxSecurity
@Configuration(proxyBeanMethods = false)
static class SecurityWebFilterChainConfig {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import reactor.core.publisher.Flux;

import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.autoconfigure.security.reactive.ReactiveSecurityAutoConfiguration.EnableWebFluxSecurityConfiguration;
import org.springframework.boot.test.context.FilteredClassLoader;
import org.springframework.boot.test.context.runner.ReactiveWebApplicationContextRunner;
import org.springframework.context.annotation.Bean;
Expand Down Expand Up @@ -47,6 +48,13 @@ void backsOffWhenWebFilterChainProxyBeanPresent() {
.run((context) -> assertThat(context).hasSingleBean(WebFilterChainProxy.class));
}

@Test
void backsOffWhenReactiveAuthenticationManagerNotPresent() {
this.contextRunner.withConfiguration(AutoConfigurations.of(ReactiveSecurityAutoConfiguration.class))
.run((context) -> assertThat(context).hasSingleBean(ReactiveSecurityAutoConfiguration.class)
.doesNotHaveBean(EnableWebFluxSecurityConfiguration.class));
}

@Test
void enablesWebFluxSecurity() {
this.contextRunner
Expand Down

0 comments on commit ee9c745

Please sign in to comment.