Browse Source

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
pull/38731/head
Andy Wilkinson 2 years ago
parent
commit
beba1f176a
  1. 6
      spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/reactive/ReactiveOAuth2ResourceServerAutoConfiguration.java
  2. 26
      spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/reactive/ReactiveOAuth2ResourceServerConfiguration.java
  3. 2
      spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/reactive/ReactiveOAuth2ResourceServerJwkConfiguration.java
  4. 2
      spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/reactive/ReactiveOAuth2ResourceServerOpaqueTokenConfiguration.java
  5. 11
      spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/oauth2/resource/reactive/ReactiveOAuth2ResourceServerAutoConfigurationTests.java

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

@ -1,5 +1,5 @@ @@ -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.
@ -40,7 +40,9 @@ import org.springframework.security.config.annotation.web.reactive.EnableWebFlux @@ -40,7 +40,9 @@ import org.springframework.security.config.annotation.web.reactive.EnableWebFlux
@ConditionalOnClass({ EnableWebFluxSecurity.class })
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE)
@Import({ ReactiveOAuth2ResourceServerConfiguration.JwtConfiguration.class,
ReactiveOAuth2ResourceServerConfiguration.OpaqueTokenConfiguration.class })
ReactiveOAuth2ResourceServerConfiguration.OpaqueTokenConfiguration.class,
ReactiveOAuth2ResourceServerConfiguration.JwtWebSecurityConfiguration.class,
ReactiveOAuth2ResourceServerConfiguration.OpaqueTokenWebSecurityConfiguration.class })
public class ReactiveOAuth2ResourceServerAutoConfiguration {
}

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

@ -1,5 +1,5 @@ @@ -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.
@ -24,8 +24,8 @@ import org.springframework.security.oauth2.server.resource.authentication.Bearer @@ -24,8 +24,8 @@ import org.springframework.security.oauth2.server.resource.authentication.Bearer
import org.springframework.security.oauth2.server.resource.introspection.ReactiveOpaqueTokenIntrospector;
/**
* Configuration classes for OAuth2 Resource Server These should be {@code @Import} in a
* regular auto-configuration class to guarantee their order of execution.
* Configuration classes for OAuth2 Resource Server. These should be {@code @Import}ed in
* a regular auto-configuration class to guarantee their order of execution.
*
* @author Madhura Bhave
*/
@ -33,18 +33,30 @@ class ReactiveOAuth2ResourceServerConfiguration { @@ -33,18 +33,30 @@ class ReactiveOAuth2ResourceServerConfiguration {
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ BearerTokenAuthenticationToken.class, ReactiveJwtDecoder.class })
@Import({ ReactiveOAuth2ResourceServerJwkConfiguration.JwtConfiguration.class,
ReactiveOAuth2ResourceServerJwkConfiguration.WebSecurityConfiguration.class })
@Import(ReactiveOAuth2ResourceServerJwkConfiguration.JwtConfiguration.class)
static class JwtConfiguration {
}
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ BearerTokenAuthenticationToken.class, ReactiveJwtDecoder.class })
@Import(ReactiveOAuth2ResourceServerJwkConfiguration.WebSecurityConfiguration.class)
static class JwtWebSecurityConfiguration {
}
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ BearerTokenAuthenticationToken.class, ReactiveOpaqueTokenIntrospector.class })
@Import({ ReactiveOAuth2ResourceServerOpaqueTokenConfiguration.OpaqueTokenIntrospectionClientConfiguration.class,
ReactiveOAuth2ResourceServerOpaqueTokenConfiguration.WebSecurityConfiguration.class })
@Import(ReactiveOAuth2ResourceServerOpaqueTokenConfiguration.OpaqueTokenIntrospectionClientConfiguration.class)
static class OpaqueTokenConfiguration {
}
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ BearerTokenAuthenticationToken.class, ReactiveOpaqueTokenIntrospector.class })
@Import(ReactiveOAuth2ResourceServerOpaqueTokenConfiguration.WebSecurityConfiguration.class)
static class OpaqueTokenWebSecurityConfiguration {
}
}

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

@ -164,11 +164,11 @@ class ReactiveOAuth2ResourceServerJwkConfiguration { @@ -164,11 +164,11 @@ class ReactiveOAuth2ResourceServerJwkConfiguration {
}
@Configuration(proxyBeanMethods = false)
@ConditionalOnBean(ReactiveJwtDecoder.class)
@ConditionalOnMissingBean(SecurityWebFilterChain.class)
static class WebSecurityConfiguration {
@Bean
@ConditionalOnBean(ReactiveJwtDecoder.class)
SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http, ReactiveJwtDecoder jwtDecoder) {
http.authorizeExchange((exchanges) -> exchanges.anyExchange().authenticated());
http.oauth2ResourceServer((server) -> customDecoder(server, jwtDecoder));

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

@ -56,10 +56,10 @@ class ReactiveOAuth2ResourceServerOpaqueTokenConfiguration { @@ -56,10 +56,10 @@ class ReactiveOAuth2ResourceServerOpaqueTokenConfiguration {
@Configuration(proxyBeanMethods = false)
@ConditionalOnMissingBean(SecurityWebFilterChain.class)
@ConditionalOnBean(ReactiveOpaqueTokenIntrospector.class)
static class WebSecurityConfiguration {
@Bean
@ConditionalOnBean(ReactiveOpaqueTokenIntrospector.class)
SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
http.authorizeExchange((exchanges) -> exchanges.anyExchange().authenticated());
http.oauth2ResourceServer((resourceServer) -> resourceServer.opaqueToken(withDefaults()));

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

@ -43,6 +43,8 @@ import org.mockito.InOrder; @@ -43,6 +43,8 @@ import org.mockito.InOrder;
import reactor.core.publisher.Mono;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener;
import org.springframework.boot.logging.LogLevel;
import org.springframework.boot.test.context.FilteredClassLoader;
import org.springframework.boot.test.context.assertj.AssertableReactiveWebApplicationContext;
import org.springframework.boot.test.context.runner.ReactiveWebApplicationContextRunner;
@ -73,6 +75,7 @@ import org.springframework.security.oauth2.server.resource.authentication.Opaque @@ -73,6 +75,7 @@ import org.springframework.security.oauth2.server.resource.authentication.Opaque
import org.springframework.security.oauth2.server.resource.introspection.ReactiveOpaqueTokenIntrospector;
import org.springframework.security.web.server.MatcherSecurityWebFilterChain;
import org.springframework.security.web.server.SecurityWebFilterChain;
import org.springframework.security.web.server.WebFilterChainProxy;
import org.springframework.security.web.server.authentication.AuthenticationWebFilter;
import org.springframework.test.util.ReflectionTestUtils;
import org.springframework.web.server.WebFilter;
@ -116,10 +119,16 @@ class ReactiveOAuth2ResourceServerAutoConfigurationTests { @@ -116,10 +119,16 @@ class ReactiveOAuth2ResourceServerAutoConfigurationTests {
}
}
@Test
void autoConfigurationDoesNotEnableWebSecurityWithoutJwtDecoderOrTokenIntrospector() {
this.contextRunner.run((context) -> assertThat(context).doesNotHaveBean(WebFilterChainProxy.class));
}
@Test
void autoConfigurationShouldConfigureResourceServer() {
this.contextRunner
.withPropertyValues("spring.security.oauth2.resourceserver.jwt.jwk-set-uri=https://jwk-set-uri.com")
.withInitializer(ConditionEvaluationReportLoggingListener.forLogLevel(LogLevel.INFO))
.run((context) -> {
assertThat(context).hasSingleBean(NimbusReactiveJwtDecoder.class);
assertFilterConfiguredWithJwtAuthenticationManager(context);
@ -385,7 +394,7 @@ class ReactiveOAuth2ResourceServerAutoConfigurationTests { @@ -385,7 +394,7 @@ class ReactiveOAuth2ResourceServerAutoConfigurationTests {
@Test
void autoConfigurationWhenIntrospectionUriAvailableShouldConfigureIntrospectionClient() {
this.contextRunner
this.contextRunner.withInitializer(ConditionEvaluationReportLoggingListener.forLogLevel(LogLevel.INFO))
.withPropertyValues(
"spring.security.oauth2.resourceserver.opaquetoken.introspection-uri=https://check-token.com",
"spring.security.oauth2.resourceserver.opaquetoken.client-id=my-client-id",

Loading…
Cancel
Save