Skip to content

Commit beba1f1

Browse files
committed
Do not enable WebFlux security unless other configuration is active
Following the changes in gh-37504, the reactive resource server auto-configuration could enable WebFlux security in situations where it was otherwise in active. This could then result in an application failing to start as no authentication manager is available. This commit updates the configurations that enable WebFlux security so that they fully back off unless their related configurations are active. Previously, only the configuration of the SecurityWebFilterChain would back off. This has been expanded to cover `@EnableWebFluxSecurity` as well. This has required splitting the configuration classes up so that the condition evaluation order can be controlled more precisely. We need to ensure that the JWT decoder bean or the opaque token introspector bean has been defined before evaluation of the conditions for `@EnableWebFluxSecurity`. Without this control, the import through `@EnableWebFluxSecurity` in one location where the conditions do not matchcan prevent a successful import in another where they do. Fixes gh-38713
1 parent 6330190 commit beba1f1

File tree

5 files changed

+35
-12
lines changed

5 files changed

+35
-12
lines changed

spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/reactive/ReactiveOAuth2ResourceServerAutoConfiguration.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2022 the original author or authors.
2+
* Copyright 2012-2023 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -40,7 +40,9 @@
4040
@ConditionalOnClass({ EnableWebFluxSecurity.class })
4141
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE)
4242
@Import({ ReactiveOAuth2ResourceServerConfiguration.JwtConfiguration.class,
43-
ReactiveOAuth2ResourceServerConfiguration.OpaqueTokenConfiguration.class })
43+
ReactiveOAuth2ResourceServerConfiguration.OpaqueTokenConfiguration.class,
44+
ReactiveOAuth2ResourceServerConfiguration.JwtWebSecurityConfiguration.class,
45+
ReactiveOAuth2ResourceServerConfiguration.OpaqueTokenWebSecurityConfiguration.class })
4446
public class ReactiveOAuth2ResourceServerAutoConfiguration {
4547

4648
}

spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/reactive/ReactiveOAuth2ResourceServerConfiguration.java

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2022 the original author or authors.
2+
* Copyright 2012-2023 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -24,27 +24,39 @@
2424
import org.springframework.security.oauth2.server.resource.introspection.ReactiveOpaqueTokenIntrospector;
2525

2626
/**
27-
* Configuration classes for OAuth2 Resource Server These should be {@code @Import} in a
28-
* regular auto-configuration class to guarantee their order of execution.
27+
* Configuration classes for OAuth2 Resource Server. These should be {@code @Import}ed in
28+
* a regular auto-configuration class to guarantee their order of execution.
2929
*
3030
* @author Madhura Bhave
3131
*/
3232
class ReactiveOAuth2ResourceServerConfiguration {
3333

3434
@Configuration(proxyBeanMethods = false)
3535
@ConditionalOnClass({ BearerTokenAuthenticationToken.class, ReactiveJwtDecoder.class })
36-
@Import({ ReactiveOAuth2ResourceServerJwkConfiguration.JwtConfiguration.class,
37-
ReactiveOAuth2ResourceServerJwkConfiguration.WebSecurityConfiguration.class })
36+
@Import(ReactiveOAuth2ResourceServerJwkConfiguration.JwtConfiguration.class)
3837
static class JwtConfiguration {
3938

4039
}
4140

41+
@Configuration(proxyBeanMethods = false)
42+
@ConditionalOnClass({ BearerTokenAuthenticationToken.class, ReactiveJwtDecoder.class })
43+
@Import(ReactiveOAuth2ResourceServerJwkConfiguration.WebSecurityConfiguration.class)
44+
static class JwtWebSecurityConfiguration {
45+
46+
}
47+
4248
@Configuration(proxyBeanMethods = false)
4349
@ConditionalOnClass({ BearerTokenAuthenticationToken.class, ReactiveOpaqueTokenIntrospector.class })
44-
@Import({ ReactiveOAuth2ResourceServerOpaqueTokenConfiguration.OpaqueTokenIntrospectionClientConfiguration.class,
45-
ReactiveOAuth2ResourceServerOpaqueTokenConfiguration.WebSecurityConfiguration.class })
50+
@Import(ReactiveOAuth2ResourceServerOpaqueTokenConfiguration.OpaqueTokenIntrospectionClientConfiguration.class)
4651
static class OpaqueTokenConfiguration {
4752

4853
}
4954

55+
@Configuration(proxyBeanMethods = false)
56+
@ConditionalOnClass({ BearerTokenAuthenticationToken.class, ReactiveOpaqueTokenIntrospector.class })
57+
@Import(ReactiveOAuth2ResourceServerOpaqueTokenConfiguration.WebSecurityConfiguration.class)
58+
static class OpaqueTokenWebSecurityConfiguration {
59+
60+
}
61+
5062
}

spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/reactive/ReactiveOAuth2ResourceServerJwkConfiguration.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,11 +164,11 @@ SupplierReactiveJwtDecoder jwtDecoderByIssuerUri(
164164
}
165165

166166
@Configuration(proxyBeanMethods = false)
167+
@ConditionalOnBean(ReactiveJwtDecoder.class)
167168
@ConditionalOnMissingBean(SecurityWebFilterChain.class)
168169
static class WebSecurityConfiguration {
169170

170171
@Bean
171-
@ConditionalOnBean(ReactiveJwtDecoder.class)
172172
SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http, ReactiveJwtDecoder jwtDecoder) {
173173
http.authorizeExchange((exchanges) -> exchanges.anyExchange().authenticated());
174174
http.oauth2ResourceServer((server) -> customDecoder(server, jwtDecoder));

spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/reactive/ReactiveOAuth2ResourceServerOpaqueTokenConfiguration.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,10 +56,10 @@ SpringReactiveOpaqueTokenIntrospector opaqueTokenIntrospector(OAuth2ResourceServ
5656

5757
@Configuration(proxyBeanMethods = false)
5858
@ConditionalOnMissingBean(SecurityWebFilterChain.class)
59+
@ConditionalOnBean(ReactiveOpaqueTokenIntrospector.class)
5960
static class WebSecurityConfiguration {
6061

6162
@Bean
62-
@ConditionalOnBean(ReactiveOpaqueTokenIntrospector.class)
6363
SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
6464
http.authorizeExchange((exchanges) -> exchanges.anyExchange().authenticated());
6565
http.oauth2ResourceServer((resourceServer) -> resourceServer.opaqueToken(withDefaults()));

spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/oauth2/resource/reactive/ReactiveOAuth2ResourceServerAutoConfigurationTests.java

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@
4343
import reactor.core.publisher.Mono;
4444

4545
import org.springframework.boot.autoconfigure.AutoConfigurations;
46+
import org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener;
47+
import org.springframework.boot.logging.LogLevel;
4648
import org.springframework.boot.test.context.FilteredClassLoader;
4749
import org.springframework.boot.test.context.assertj.AssertableReactiveWebApplicationContext;
4850
import org.springframework.boot.test.context.runner.ReactiveWebApplicationContextRunner;
@@ -73,6 +75,7 @@
7375
import org.springframework.security.oauth2.server.resource.introspection.ReactiveOpaqueTokenIntrospector;
7476
import org.springframework.security.web.server.MatcherSecurityWebFilterChain;
7577
import org.springframework.security.web.server.SecurityWebFilterChain;
78+
import org.springframework.security.web.server.WebFilterChainProxy;
7679
import org.springframework.security.web.server.authentication.AuthenticationWebFilter;
7780
import org.springframework.test.util.ReflectionTestUtils;
7881
import org.springframework.web.server.WebFilter;
@@ -116,10 +119,16 @@ void cleanup() throws Exception {
116119
}
117120
}
118121

122+
@Test
123+
void autoConfigurationDoesNotEnableWebSecurityWithoutJwtDecoderOrTokenIntrospector() {
124+
this.contextRunner.run((context) -> assertThat(context).doesNotHaveBean(WebFilterChainProxy.class));
125+
}
126+
119127
@Test
120128
void autoConfigurationShouldConfigureResourceServer() {
121129
this.contextRunner
122130
.withPropertyValues("spring.security.oauth2.resourceserver.jwt.jwk-set-uri=https://jwk-set-uri.com")
131+
.withInitializer(ConditionEvaluationReportLoggingListener.forLogLevel(LogLevel.INFO))
123132
.run((context) -> {
124133
assertThat(context).hasSingleBean(NimbusReactiveJwtDecoder.class);
125134
assertFilterConfiguredWithJwtAuthenticationManager(context);
@@ -385,7 +394,7 @@ void autoConfigurationWhenSecurityWebFilterChainConfigPresentShouldNotAddOne() {
385394

386395
@Test
387396
void autoConfigurationWhenIntrospectionUriAvailableShouldConfigureIntrospectionClient() {
388-
this.contextRunner
397+
this.contextRunner.withInitializer(ConditionEvaluationReportLoggingListener.forLogLevel(LogLevel.INFO))
389398
.withPropertyValues(
390399
"spring.security.oauth2.resourceserver.opaquetoken.introspection-uri=https://check-token.com",
391400
"spring.security.oauth2.resourceserver.opaquetoken.client-id=my-client-id",

0 commit comments

Comments
 (0)