Skip to content

Nested bean instance supplier invocation does not retain previous factory method #33180

Closed
@ditogam

Description

Hello,

I have a problem when running native build

here is my build.gradle

./gradlew --version

------------------------------------------------------------
Gradle 8.8
------------------------------------------------------------

Build time:   2024-05-31 21:46:56 UTC
Revision:     4bd1b3d3fc3f31db5a26eecb416a165b8cc36082

Kotlin:       1.9.22
Groovy:       3.0.21
Ant:          Apache Ant(TM) version 1.10.13 compiled on January 4 2023
JVM:          21.0.3 (Oracle Corporation 21.0.3+7-LTS-jvmci-23.1-b37)
OS:           Linux 6.8.0-36-generic amd64

uname -a
Linux homepc 6.8.0-36-generic spring-projects/spring-boot#36-Ubuntu SMP PREEMPT_DYNAMIC Mon Jun 10 10:49:14 UTC 2024 x86_64 x86_64 x86_64 GNU/Linux

cat /etc/os-release

PRETTY_NAME="Ubuntu 24.04 LTS"
NAME="Ubuntu"
VERSION_ID="24.04"
VERSION="24.04 LTS (Noble Numbat)"
VERSION_CODENAME=noble
ID=ubuntu
ID_LIKE=debian
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
UBUNTU_CODENAME=noble
LOGO=ubuntu-logo

Application code:

package org.example.testsb;

import java.io.Serializable;

import lombok.RequiredArgsConstructor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.Advisor;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Role;
import org.springframework.core.annotation.Order;
import org.springframework.security.access.PermissionEvaluator;
import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler;
import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler;
import org.springframework.security.authorization.AuthorizationEventPublisher;
import org.springframework.security.authorization.AuthorizationManager;
import org.springframework.security.authorization.SpringAuthorizationEventPublisher;
import org.springframework.security.authorization.method.AuthorizationManagerBeforeMethodInterceptor;
import org.springframework.security.authorization.method.PreAuthorizeAuthorizationManager;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Service;

import static org.springframework.beans.factory.config.BeanDefinition.ROLE_INFRASTRUCTURE;

@SpringBootApplication
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

    @Service
    public static class PermissionEvaluatorTest implements PermissionEvaluator {

        public boolean hasPermission(Authentication authentication, Object targetDomainObject, Object permission) {
            return false;
        }

        public boolean hasPermission(Authentication authentication, Serializable targetId, String targetType, Object permission) {
            return false;
        }
    }

    @EnableWebSecurity
    @EnableMethodSecurity(
        securedEnabled = true,
        proxyTargetClass = true
    )
    @RequiredArgsConstructor
    @Configuration
    public static class SecurityConfig {

        @Configuration
        @Order(2)
        @RequiredArgsConstructor
        public static class OAuthSecurityConfig {
            @Bean
            public AuthorizationEventPublisher authorizationEventPublisher(ApplicationEventPublisher publisher) {
                return new SpringAuthorizationEventPublisher(publisher);
            }

            @Bean
            public MethodSecurityExpressionHandler methodSecurityExpressionHandler(PermissionEvaluator permissionEvaluator) {
                DefaultMethodSecurityExpressionHandler handler = new DefaultMethodSecurityExpressionHandler();
                handler.setPermissionEvaluator(permissionEvaluator);

                return handler;
            }

            @Bean
            public AuthorizationManager<MethodInvocation> authorizationManager(
                MethodSecurityExpressionHandler methodSecurityExpressionHandler) {
                PreAuthorizeAuthorizationManager preAuthorizeAuthorizationManager = new PreAuthorizeAuthorizationManager();
                preAuthorizeAuthorizationManager.setExpressionHandler(methodSecurityExpressionHandler);

                return (authentication, object) -> null;
            }

            @Bean
            @Role(ROLE_INFRASTRUCTURE)
            public Advisor authorizationManagerBeforeMethodInterception(AuthorizationManager<MethodInvocation> authorizationManager,
                                                                        AuthorizationEventPublisher publisher) {
                AuthorizationManagerBeforeMethodInterceptor authorizationManagerBeforeMethodInterceptor =
                    AuthorizationManagerBeforeMethodInterceptor.preAuthorize(authorizationManager);
                authorizationManagerBeforeMethodInterceptor.setAuthorizationEventPublisher(publisher);

                return authorizationManagerBeforeMethodInterceptor;
            }
        }
    }
}

I'm compiling using ./gradlew nativeCompile

build/native/nativeCompile/testSB

Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'methodSecurityExpressionHandler': null
        at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:648)
        at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:636)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1337)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1167)

Full error log error.log

Here are generated aot resources
resources.zip

if I remove implements PermissionEvaluator from PermissionEvaluatorTest, inject it in OAuthSecurityConfig

private final PermissionEvaluatorTest permissionEvaluatorTest;
and create bean


@Bean
        public PermissionEvaluator permissionEvaluator() {
            return new PermissionEvaluator() {
                @Override
                public boolean hasPermission(Authentication authentication, Object targetDomainObject, Object permission) {
                    return permissionEvaluatorTest.hasPermission(authentication, targetDomainObject, permission);
                }

                @Override
                public boolean hasPermission(Authentication authentication, Serializable targetId, String targetType, Object permission) {
                    return permissionEvaluatorTest.hasPermission(authentication, targetId, targetType, permission);
                }
            };
        }

then it works as expected

Full working code

package org.example.testsb;

import java.io.Serializable;

import lombok.RequiredArgsConstructor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.Advisor;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Role;
import org.springframework.core.annotation.Order;
import org.springframework.security.access.PermissionEvaluator;
import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler;
import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler;
import org.springframework.security.authorization.AuthorizationEventPublisher;
import org.springframework.security.authorization.AuthorizationManager;
import org.springframework.security.authorization.SpringAuthorizationEventPublisher;
import org.springframework.security.authorization.method.AuthorizationManagerBeforeMethodInterceptor;
import org.springframework.security.authorization.method.PreAuthorizeAuthorizationManager;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Service;

import static org.springframework.beans.factory.config.BeanDefinition.ROLE_INFRASTRUCTURE;

@SpringBootApplication
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

    @Service
    public static class PermissionEvaluatorTest {

        public boolean hasPermission(Authentication authentication, Object targetDomainObject, Object permission) {
            return false;
        }

        public boolean hasPermission(Authentication authentication, Serializable targetId, String targetType, Object permission) {
            return false;
        }
    }

    @EnableWebSecurity
    @EnableMethodSecurity(
        securedEnabled = true,
        proxyTargetClass = true
    )
    @RequiredArgsConstructor
    @Configuration
    public static class SecurityConfig {

        @Configuration
        @Order(2)
        @RequiredArgsConstructor
        public static class OAuthSecurityConfig {
            private final PermissionEvaluatorTest permissionEvaluatorTest;

            @Bean
            public PermissionEvaluator permissionEvaluator() {
                return new PermissionEvaluator() {
                    @Override
                    public boolean hasPermission(Authentication authentication, Object targetDomainObject, Object permission) {
                        return permissionEvaluatorTest.hasPermission(authentication, targetDomainObject, permission);
                    }

                    @Override
                    public boolean hasPermission(Authentication authentication, Serializable targetId, String targetType, Object permission) {
                        return permissionEvaluatorTest.hasPermission(authentication, targetId, targetType, permission);
                    }
                };
            }

            @Bean
            public AuthorizationEventPublisher authorizationEventPublisher(ApplicationEventPublisher publisher) {
                return new SpringAuthorizationEventPublisher(publisher);
            }

            @Bean
            public MethodSecurityExpressionHandler methodSecurityExpressionHandler(PermissionEvaluator permissionEvaluator) {
                DefaultMethodSecurityExpressionHandler handler = new DefaultMethodSecurityExpressionHandler();
                handler.setPermissionEvaluator(permissionEvaluator);

                return handler;
            }

            @Bean
            public AuthorizationManager<MethodInvocation> authorizationManager(
                MethodSecurityExpressionHandler methodSecurityExpressionHandler) {
                PreAuthorizeAuthorizationManager preAuthorizeAuthorizationManager = new PreAuthorizeAuthorizationManager();
                preAuthorizeAuthorizationManager.setExpressionHandler(methodSecurityExpressionHandler);

                return (authentication, object) -> null;
            }

            @Bean
            @Role(ROLE_INFRASTRUCTURE)
            public Advisor authorizationManagerBeforeMethodInterception(AuthorizationManager<MethodInvocation> authorizationManager,
                                                                        AuthorizationEventPublisher publisher) {
                AuthorizationManagerBeforeMethodInterceptor authorizationManagerBeforeMethodInterceptor =
                    AuthorizationManagerBeforeMethodInterceptor.preAuthorize(authorizationManager);
                authorizationManagerBeforeMethodInterceptor.setAuthorizationEventPublisher(publisher);

                return authorizationManagerBeforeMethodInterceptor;
            }
        }
    }
}

It was perfectly worked in '3.2.5', but not works from '3.3.0'

Metadata

Labels

in: coreIssues in core modules (aop, beans, core, context, expression)status: backportedAn issue that has been backported to maintenance branchestype: bugA general bug

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions