SpringOpaqueTokenIntrospector is configured before my custom OpaqueTokenIntrospector

74 Views Asked by At

I am upgrading an app from Spring Boot 2.x to 3.

The app has OAuth2 implemented and is configured as a Resource Server.

When converting the security configuration to how it's defined here, I need a custom Introspector to override SB's own autoconfigured one. Still, it looks like my configuration is not picked up and the tests keep failing with exception

org.springframework.beans.factory.BeanCreationException:
Error creating bean with name 'opaqueTokenIntrospector' defined in class path resource
[org/springframework/boot/autoconfigure/security/oauth2/resource/servlet/OAuth2ResourceServerOpaqueTokenConfiguration$OpaqueTokenIntrospectionClientConfiguration.class]:
Failed to instantiate
[org.springframework.security.oauth2.server.resource.introspection.SpringOpaqueTokenIntrospector]:
Factory method 'opaqueTokenIntrospector' threw exception with message:
clientId cannot be null

@ConditionalOnMissingBean(OpaqueTokenIntrospector.class) annotation should only work when there is no other bean of type OpaqueTokenIntrospector, and @ConditionalOnProperty(name = "spring.security.oauth2.resourceserver.opaquetoken.introspection-uri") annotation is placed on the class SpringOpaqueTokenIntrospector.

CustomIntrospector I want configured.

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.security.oauth2.resource.OAuth2ResourceServerProperties;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.Profile;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.oauth2.server.resource.introspection.OpaqueTokenIntrospector;
import org.springframework.security.web.SecurityFilterChain;

@Configuration
@EnableWebSecurity
@Profile("!local")
public class SecurityConfiguration {

    private SecurityHandler securityHandler;

    @Autowired
    public SecurityConfiguration(final SecurityHandler securityHandler) {
        this.securityHandler = securityHandler;
    }

    @Bean
    public SecurityFilterChain configure(final HttpSecurity http) throws Exception {
        http
            .exceptionHandling(exception ->
                exception.accessDeniedHandler(securityHandler)
                    .authenticationEntryPoint(securityHandler))
            .sessionManagement(session ->
                    session.sessionCreationPolicy(SessionCreationPolicy.NEVER))
            .httpBasic(AbstractHttpConfigurer::disable)
            .oauth2ResourceServer(server ->
                server.opaqueToken(Customizer.withDefaults()))
            .authorizeHttpRequests(authorize ->
                authorize.requestMatchers("/actuator/**").permitAll()
                    .requestMatchers("/jolokia/**").permitAll());
        return http.build();
    }

    @Bean
    public OpaqueTokenIntrospector introspector(final OAuth2ResourceServerProperties resourceServerProperties, final RestTemplateBuilder rtBuilder) {
        final String introspectionUri = resourceServerProperties.getOpaquetoken().getIntrospectionUri();
        return new CustomOpaqueTokenIntrospector(introspectionUri, rtBuilder.build());
    }
}

application.yaml

spring.security.oauth2.resourceserver.opaquetoken.introspection-uri: https://idp.example.com/introspect

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.ateebahmed</groupId>
    <artifactId>my-secure-service</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.0.13</version>
        <relativePath /> <!-- lookup parent from repository -->
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>17</java.version>
    </properties>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>2022.0.4</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <dependencies>
        <!--Spring-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-circuitbreaker-resilience4j</artifactId>
        </dependency>

        <!-- Jolokia -->
        <dependency>
            <groupId>org.jolokia</groupId>
            <artifactId>jolokia-support-spring</artifactId>
            <version>2.0.0</version>
        </dependency>

        <dependency>
            <groupId>io.micrometer</groupId>
            <artifactId>micrometer-registry-jmx</artifactId>
        </dependency>

        <!-- Oauth2 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
        </dependency>

        <!-- Feign -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
        <dependency>
            <groupId>io.github.openfeign</groupId>
            <artifactId>feign-httpclient</artifactId>
        </dependency>

        <dependency>
            <groupId>org.glassfish.jaxb</groupId>
            <artifactId>jaxb-runtime</artifactId>
        </dependency>

        <!-- TESTING -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-contract-wiremock</artifactId>
            <scope>test</scope>
        </dependency>

        <!-- TEST CONTAINERS -->
        <dependency>
            <groupId>org.testcontainers</groupId>
            <artifactId>junit-jupiter</artifactId>
            <version>1.16.0</version>
        </dependency>

        <!--COMMON LIBRARIES-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-validation</artifactId>
        </dependency>

    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <executions>
                    <execution>
                        <goals>
                            <goal>build-info</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <artifactId>maven-surefire-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

When I tried to run the app with client-id and client-secret as dummy values in application.yaml, it ran fine.

I tried other ways of overriding introspector as mentioned in the docs like using the introspector method, but it doesn't affect as well.

How do I make sure SB's own classes are not picked up.

If you think this is an issue with SB's autoconfiguration itself, let me know I'll create an issue there.

0

There are 0 best solutions below