The @EnableGlobalAuthentication in Spring Security uses @Configuration as a meta-annotation, which in turn uses @AliasFor. When using RuntimeHintsUtils.registerAnnotation(hints, EnableGlobalAuthentication.class) it does not work because the util method only considers @AliasFor inside the EnableGlobalAuthentication class, ignoring what is in the @Configuration, therefore leading to an error like this:
Caused by: com.oracle.svm.core.jdk.UnsupportedFeatureError: Proxy class defined by interfaces [interface org.springframework.security.config.annotation.authentication.configuration.EnableGlobalAuthentication, interface org.springframework.core.annotation.SynthesizedAnnotation] not found. Generating proxy classes at runtime is not supported. Proxy classes need to be defined at image build time by specifying the list of interfaces that they implement. To define proxy classes use -H:DynamicProxyConfigurationFiles=<comma-separated-config-files> and -H:DynamicProxyConfigurationResources=<comma-separated-config-resources> options.
If done manually, like so, it works:
hints.reflection().registerType(EnableGlobalAuthentication.class, RuntimeHintsUtils.ANNOTATION_HINT);
hints.proxies().registerJdkProxy(EnableGlobalAuthentication.class, SynthesizedAnnotation.class);
To simulate: Change the security sample this way:
- Create this class:
class CoreSecurityHints implements RuntimeHintsRegistrar {
private static final Consumer<TypeHint.Builder> HINT = builder -> builder.withMembers(
MemberCategory.INVOKE_DECLARED_CONSTRUCTORS);
@Override
public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
registerDefaultAuthenticationEventPublisherTypes(hints);
// hints.reflection().registerType(EnableGlobalAuthentication.class, RuntimeHintsUtils.ANNOTATION_HINT); it works if done this way
// hints.proxies().registerJdkProxy(EnableGlobalAuthentication.class, SynthesizedAnnotation.class);
RuntimeHintsUtils.registerAnnotation(hints, EnableGlobalAuthentication.class);
}
private void registerDefaultAuthenticationEventPublisherTypes(RuntimeHints hints) {
hints.reflection().registerType(AuthenticationFailureBadCredentialsEvent.class, HINT)
.registerType(AuthenticationFailureCredentialsExpiredEvent.class, HINT)
.registerType(AuthenticationFailureDisabledEvent.class, HINT)
.registerType(AuthenticationFailureExpiredEvent.class, HINT)
.registerType(AuthenticationFailureLockedEvent.class, HINT)
.registerType(AuthenticationFailureProviderNotFoundEvent.class, HINT)
.registerType(AuthenticationFailureProxyUntrustedEvent.class, HINT)
.registerType(AuthenticationFailureServiceExceptionEvent.class, HINT)
.registerType(AuthenticationServiceException.class, HINT)
.registerType(AccountExpiredException.class, HINT)
.registerType(BadCredentialsException.class, HINT)
.registerType(CredentialsExpiredException.class, HINT)
.registerType(DisabledException.class, HINT)
.registerType(LockedException.class, HINT)
.registerType(UsernameNotFoundException.class, HINT)
.registerType(ProviderNotFoundException.class, HINT);
}
}
- Add
@ImportRuntimeHints(CoreSecurityHints.class)in theSecuringWebApplicationclass - Run the
build.shscript
Comment From: snicoll
Thanks for the report. It is not immediately obvious to me why this annotation needs the proxy (ping @jhoeller).
Nit @marcusdacoregio but you can use registerTypes to apply the same hint on a list of classes.
Comment From: sbrannen
It is not immediately obvious to me why this annotation needs the proxy
It actually shouldn't need a proxy, since it should not be synthesized.
Though perhaps there is a bug lurking in the "synthesizable" computation algorithm in AnnotationTypeMapping:
https://github.com/spring-projects/spring-framework/blob/d1b65f6d3e90af3f55d1b4d347afb3ebe9a3de44/spring-core/src/main/java/org/springframework/core/annotation/AnnotationTypeMapping.java#L314-L352
Comment From: sbrannen
Thanks for bringing this to our attention, @marcusdacoregio.
It turns out it is in fact a regression in our merged annotation support.
I am therefore closing this issue as superseded by #28704.