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'
Comment From: wilkinsona
I suspect that this is due to a change in Spring Security or, perhaps, Spring Framework, but I cannot tell for certain based on what you have provided thus far. Unfortunately, the assorted code snippets don't provide the full picture. If you would like us to spend some more time investigating, please spend some time providing a complete yet minimal sample that reproduces the problem. You can share it with us by pushing it to a separate repository on GitHub or by zipping it up and attaching it to this issue.
Comment From: ditogam
Hi @wilkinsona Working branch Branch that has issue The same code as in branch with issue, but with springboot 3.2.5 Working with 3.2.5, it starts as expected
Comment From: wilkinsona
Thanks for the sample. The problem also occurs with Spring Boot 3.2.6. It isn't specifically tied to native as an AOT-processed context running on the JVM will also fail in the same way.
With Spring Boot 3.2.6, the problem does not occur if I downgrade Spring Framework from 6.1.8 to 6.1.7, as such this appears to be a Spring Framework regression. We'll transfer this issue to their issue tracker so that they can investigate further.
Comment From: ditogam
Thank you for reply, correct, I couldn't upgrade our codbase version after 3.2.5
Comment From: wilkinsona
Sorry, it working with 6.1.7 vs 6.1.8 wasn't entirely accurate. For that to be the case, you have to declare some of the @Bean
methods as static
. As presented, the sample only works if you downgrade from 6.1.8 to 6.1.6. While experimenting, I've also noticed that it works with 6.1.8 if all of the app's @Bean
methods are static and the dependencies of authorizationManagerBeforeMethodInterception
are @Lazy
:
@Configuration
@Order(2)
public static class OAuthSecurityConfig {
@Bean
public static AuthorizationEventPublisher authorizationEventPublisher(ApplicationEventPublisher publisher) {
return new SpringAuthorizationEventPublisher(publisher);
}
@Bean
public static MethodSecurityExpressionHandler methodSecurityExpressionHandler(PermissionEvaluator permissionEvaluator) {
DefaultMethodSecurityExpressionHandler handler = new DefaultMethodSecurityExpressionHandler();
handler.setPermissionEvaluator(permissionEvaluator);
return handler;
}
@Bean
public static AuthorizationManager<MethodInvocation> authorizationManager(
MethodSecurityExpressionHandler methodSecurityExpressionHandler) {
PreAuthorizeAuthorizationManager preAuthorizeAuthorizationManager = new PreAuthorizeAuthorizationManager();
preAuthorizeAuthorizationManager.setExpressionHandler(methodSecurityExpressionHandler);
return (authentication, object) -> null;
}
@Bean
@Role(ROLE_INFRASTRUCTURE)
public static Advisor authorizationManagerBeforeMethodInterception(@Lazy AuthorizationManager<MethodInvocation> authorizationManager,
@Lazy AuthorizationEventPublisher publisher) {
AuthorizationManagerBeforeMethodInterceptor authorizationManagerBeforeMethodInterceptor =
AuthorizationManagerBeforeMethodInterceptor.preAuthorize(authorizationManager);
authorizationManagerBeforeMethodInterceptor.setAuthorizationEventPublisher(publisher);
return authorizationManagerBeforeMethodInterceptor;
}
}
Comment From: snicoll
Thanks to @jhoeller we went to the bottom of this. We're unclear why this scenario worked before but it's a problem with BeanInstanceSupplier
that does not restore the previous factory method in case of nested invocations.