Skip to content

Spring Boot 3.2 app that uses WebFlux, Security, and Actuator may fail to start due to a missing authentication manager #39096

@razvdana

Description

@razvdana

After migration to spring boot 3.2.1 (from 3.1.2) I'm facing the following issue:

    Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate 
    [org.springframework.security.web.server.SecurityWebFilterChain]: Factory method 'springSecurityFilterChain' threw 
    exception with message: authenticationManager cannot be null
        	at 
    org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:177) ~ 
   [spring-beans-6.1.2.jar:6.1.2]
        	at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:651) ~ 
   [spring-beans-6.1.2.jar:6.1.2]
        	... 143 common frames omitted
    Caused by: java.lang.IllegalArgumentException: authenticationManager cannot be null
        	at org.springframework.util.Assert.notNull(Assert.java:172) ~[spring-core-6.1.2.jar:6.1.2]
	        at org.springframework.security.web.server.authentication.AuthenticationWebFilter.<init> 
   (AuthenticationWebFilter.java:94) ~[spring-security-web-6.2.1.jar:6.2.1]
        	at 
    org.springframework.security.config.web.server.ServerHttpSecurity$HttpBasicSpec.configure(ServerHttpSecurity.java:2305) 
    ~[spring-security-config-6.2.1.jar:6.2.1]
        	at org.springframework.security.config.web.server.ServerHttpSecurity.build(ServerHttpSecurity.java:1545) ~[spring- 
   security-config-6.2.1.jar:6.2.1]
        	at 
org.springframework.boot.actuate.autoconfigure.security.reactive.ReactiveManagementWebSecurityAutoConfiguration.sprin gSecurityFilterChain(ReactiveManagementWebSecurityAutoConfiguration.java:69) ~[spring-boot-actuator-autoconfigure- 3.2.1.jar:3.2.1]
 	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) ~[na:na]
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
	at java.base/java.lang.reflect.Method.invoke(Method.java:568) ~[na:na]
	at 
    org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:140) ~ 
    [spring-beans-6.1.2.jar:6.1.2]
	... 144 common frames omitted

Relevant dependencies:

       <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-webflux</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-validation</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-cache</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
		<dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt-api</artifactId>
            <version>${jwt.version}</version>
        </dependency>
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt-impl</artifactId>
            <version>${jwt.version}</version>
        </dependency>
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt-gson</artifactId>
            <version>${jwt.version}</version>
        </dependency>
        <dependency>
            <groupId>io.github.openfeign.form</groupId>
            <artifactId>feign-form</artifactId>
            <version>${feign-form.version}</version>
        </dependency>
        <dependency>
            <groupId>com.github.ben-manes.caffeine</groupId>
            <artifactId>caffeine</artifactId>
            <version>${caffeine.version}</version>
        </dependency>
        <dependency>
            <groupId>com.playtika.reactivefeign</groupId>
            <artifactId>feign-reactor-spring-cloud-starter</artifactId>
            <version>${reactive-feign.version}</version>
            <type>pom</type>
        </dependency>

Configuration file:

@Configuration
@ComponentScan(basePackageClasses = SecurityConfig.class)
@EnableReactiveMethodSecurity
@EnableWebFluxSecurity
@EnableConfigurationProperties({ ClientProperties.class, MultipleIssuersProperties.class })
class SecurityConfig {

    AuthenticationExceptionEntryPoint authenticationEntryPoint; // autowired
    Issuer mainIssuer;
    MultipleIssuersProperties issuers;
    List<String> whitelistedUrls;
    List<String> allowedOrigins;

   // Constructor omitted

    @Bean
    public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
        return http
                .authorizeExchange(exchange -> exchange
                    .pathMatchers(whitelistedUrls.toArray(String[]::new))
                    .permitAll()
                    .anyExchange().authenticated())
                .cors(cors -> cors.configurationSource(corsConfigurationSource()))
                .csrf(CsrfSpec::disable)
                .exceptionHandling(exception -> exception.authenticationEntryPoint(authenticationEntryPoint))
                .oauth2ResourceServer(server -> server.authenticationManagerResolver(reactiveAuthenticationManagerResolver()))
                .build();
    }

    @Bean
    public JwtIssuerReactiveAuthenticationManagerResolver reactiveAuthenticationManagerResolver() {
        var managers = new HashMap<String, ReactiveAuthenticationManager>();
        managers.put(
            mainIssuer.issuerUri(),
            new JwtReactiveAuthenticationManager(reactiveJwtDecoder())
        );

        issuers.issuers().forEach(issuer -> {
            var jwkSource = reactiveRemoteJWKSource(issuer);
            var jwtDecoder = reactiveJwtDecoder(issuer, jwkSource);
            var manager = new JwtReactiveAuthenticationManager(jwtDecoder);
            managers.put(issuer.issuerUri(), manager);

        });
        return new JwtIssuerReactiveAuthenticationManagerResolver(issuer -> justOrEmpty(managers.get(issuer)));
    }

    @Bean
    public ReactiveJwtDecoder reactiveJwtDecoder() {
        return reactiveJwtDecoder(mainIssuer, reactiveRemoteJWKSource());
    }

    // other beans omitted

Codebase remains unchanged, aside from version upgrade.

Metadata

Metadata

Assignees

Labels

type: regressionA regression from a previous release

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions