docs
Spring Security 6.0+ provides comprehensive support for Ahead-of-Time (AOT) compilation and GraalVM native image generation. This enables Spring Security applications to compile to native executables with fast startup times and reduced memory footprint.
Core Capabilities:
SecurityHintsRegistrar interfacePrePostAuthorizeHintsRegistrar and AuthorizeReturnObjectHintsRegistrarPrePostAuthorizeExpressionBeanHintsRegistrarCoreSecurityRuntimeHints (package-private, auto-registered)Key Interfaces and Classes:
SecurityHintsRegistrar - Interface: registerHints(RuntimeHints, ConfigurableListableBeanFactory)AuthorizeReturnObjectHintsRegistrar - Registers proxy hints for @AuthorizeReturnObjectAuthorizeReturnObjectCoreHintsRegistrar - Auto-discovers @AuthorizeReturnObject methodsPrePostAuthorizeHintsRegistrar - Registers hints for @PreAuthorize / @PostAuthorizePrePostAuthorizeExpressionBeanHintsRegistrar - Registers hints for beans used in expressionsRuntimeHints - Spring AOT hints API (reflection, resources, proxies, serialization)Default Behaviors:
SecurityHintsRegistrar beans)AuthorizationProxyFactory (auto-discovery available)Threading Model:
Lifecycle:
Exceptions:
ClassNotFoundException, NoSuchMethodException if hints missingEdge Cases:
PrePostAuthorizeExpressionBeanHintsRegistrarAuthorizeReturnObjectCoreHintsRegistrarBase interface for registering Spring Security runtime hints for AOT compilation.
public interface SecurityHintsRegistrar { .api }Description: Strategy interface for registering runtime hints required for native image compilation.
Methods:
void registerHints(RuntimeHints hints,
ConfigurableListableBeanFactory beanFactory) { .api }hints - RuntimeHints to register hints intobeanFactory - BeanFactory for scanning application beansPackage: org.springframework.security.aot.hint
Since: 6.4
Registers core Spring Security runtime hints.
final class CoreSecurityRuntimeHints implements RuntimeHintsRegistrar { .api }Description: Registers hints for core Spring Security classes required at runtime.
Registered Hints:
Resource Hints:
org.springframework.security.messages - Message bundlesReflection Hints:
AuthenticationException subclassesPackage: org.springframework.security.aot.hint (package-private)
Since: 6.0
Registered Classes:
// Authentication exceptions
- BadCredentialsException
- UsernameNotFoundException
- AccountExpiredException
- CredentialsExpiredException
- DisabledException
- LockedException
- InsufficientAuthenticationException
- AuthenticationServiceException
- AuthenticationCredentialsNotFoundException
- ProviderNotFoundException
// Core types
- Authentication
- GrantedAuthority
- UserDetails
- SecurityContext
// Expression classes
- SecurityExpressionRoot
- MethodSecurityExpressionOperationsRegisters hints for one-time token functionality.
final class OneTimeTokenRuntimeHints implements RuntimeHintsRegistrar { .api }Description: Registers resource hints for OTT JDBC schema.
Registered Resources:
org/springframework/security/core/ott/jdbc/one-time-tokens-schema.sqlPackage: org.springframework.security.aot.hint (package-private)
Since: 6.4
Registers hints for @AuthorizeReturnObject proxy generation.
public final class AuthorizeReturnObjectHintsRegistrar
implements SecurityHintsRegistrar { .api }Description: Scans for types requiring authorization proxy generation and registers proxy hints.
Constructors:
public AuthorizeReturnObjectHintsRegistrar(
AuthorizationProxyFactory proxyFactory,
Class<?>... targetClasses) { .api }proxyFactory - Factory for creating authorization proxiestargetClasses - Classes to register proxy hints forpublic AuthorizeReturnObjectHintsRegistrar(
AuthorizationProxyFactory proxyFactory,
List<Class<?>> targetClasses) { .api }Package: org.springframework.security.aot.hint
Since: 6.4
Example:
@Configuration
public class NativeSecurityConfig {
@Bean
public SecurityHintsRegistrar authorizeReturnObjectHints(
AuthorizationProxyFactory proxyFactory) {
return new AuthorizeReturnObjectHintsRegistrar(
proxyFactory,
Document.class,
Report.class,
UserProfile.class
);
}
}Automatically discovers beans with @AuthorizeReturnObject methods and registers hints.
public final class AuthorizeReturnObjectCoreHintsRegistrar
implements SecurityHintsRegistrar { .api }Description: Scans application beans for @AuthorizeReturnObject usage and registers proxy hints automatically.
Constructor:
public AuthorizeReturnObjectCoreHintsRegistrar(
AuthorizationProxyFactory proxyFactory) { .api }Package: org.springframework.security.aot.hint
Since: 6.4
Example:
@Configuration
public class AutoScanSecurityConfig {
@Bean
public SecurityHintsRegistrar autoScanProxyHints(
AuthorizationProxyFactory proxyFactory) {
// Automatically discovers all @AuthorizeReturnObject methods
return new AuthorizeReturnObjectCoreHintsRegistrar(proxyFactory);
}
}
// Application code - automatically discovered
@Service
public class DocumentService {
@AuthorizeReturnObject
public Document getDocument(Long id) {
return documentRepository.findById(id);
}
}Registers hints for @PreAuthorize and @PostAuthorize annotations.
public final class PrePostAuthorizeHintsRegistrar
implements SecurityHintsRegistrar { .api }Description: Scans for @PreAuthorize/@PostAuthorize annotations and registers reflection hints for method security.
Package: org.springframework.security.aot.hint
Since: 6.4
Registered Hints:
Example:
@Bean
public SecurityHintsRegistrar prePostAuthorizeHints() {
return new PrePostAuthorizeHintsRegistrar();
}Registers hints for beans referenced in security expressions.
public final class PrePostAuthorizeExpressionBeanHintsRegistrar
implements SecurityHintsRegistrar { .api }Description: Registers reflection hints for beans used in @PreAuthorize/@PostAuthorize expressions.
Constructors:
public PrePostAuthorizeExpressionBeanHintsRegistrar(
Class<?>... beanClasses) { .api }beanClasses - Classes used in security expressionspublic PrePostAuthorizeExpressionBeanHintsRegistrar(
List<Class<?>> beanClasses) { .api }Package: org.springframework.security.aot.hint
Since: 6.4
Example:
// Security expression using custom bean
@PreAuthorize("@permissionEvaluator.hasPermission(#document)")
public void updateDocument(Document document) { }
// Register hints for expression beans
@Bean
public SecurityHintsRegistrar expressionBeanHints() {
return new PrePostAuthorizeExpressionBeanHintsRegistrar(
CustomPermissionEvaluator.class,
DocumentValidator.class
);
}
@Component("permissionEvaluator")
public class CustomPermissionEvaluator {
public boolean hasPermission(Document document) {
// Permission logic
return true;
}
}Enable native image support in Spring Boot application:
@SpringBootApplication
public class NativeSecurityApplication {
public static void main(String[] args) {
SpringApplication.run(NativeSecurityApplication.class, args);
}
// Spring Security automatically registers core hints
}Maven Configuration (pom.xml):
<build>
<plugins>
<plugin>
<groupId>org.graalvm.buildtools</groupId>
<artifactId>native-maven-plugin</artifactId>
<configuration>
<imageName>${project.artifactId}</imageName>
<mainClass>${project.groupId}.NativeSecurityApplication</mainClass>
<buildArgs>
<buildArg>--no-fallback</buildArg>
<buildArg>-H:+ReportExceptionStackTraces</buildArg>
</buildArgs>
</configuration>
</plugin>
</plugins>
</build>Gradle Configuration (build.gradle):
plugins {
id 'org.graalvm.buildtools.native' version '0.9.28'
}
graalvmNative {
binaries {
main {
imageName = project.name
mainClass = 'com.example.NativeSecurityApplication'
buildArgs.add('--no-fallback')
buildArgs.add('-H:+ReportExceptionStackTraces')
}
}
}Register custom security hints:
@Configuration
public class CustomSecurityHintsConfig {
@Bean
public RuntimeHintsRegistrar customSecurityHints() {
return (hints, classLoader) -> {
// Register custom authentication classes
hints.reflection()
.registerType(CustomAuthenticationToken.class,
MemberCategory.INVOKE_PUBLIC_CONSTRUCTORS,
MemberCategory.INVOKE_PUBLIC_METHODS,
MemberCategory.DECLARED_FIELDS);
// Register custom user details
hints.reflection()
.registerType(CustomUserDetails.class,
MemberCategory.INVOKE_PUBLIC_CONSTRUCTORS,
MemberCategory.INVOKE_PUBLIC_METHODS);
// Register custom exceptions
hints.reflection()
.registerType(CustomAuthenticationException.class,
MemberCategory.INVOKE_PUBLIC_CONSTRUCTORS);
// Register resources
hints.resources()
.registerPattern("custom-security-config.properties");
// Register proxies
hints.proxies()
.registerJdkProxy(CustomAuthenticationProvider.class);
// Register serialization
hints.serialization()
.registerType(CustomAuthenticationToken.class);
};
}
}Configure hints for method security:
@Configuration
@EnableMethodSecurity
public class MethodSecurityNativeConfig {
@Bean
public SecurityHintsRegistrar methodSecurityHints(
AuthorizationProxyFactory proxyFactory) {
// Combine multiple registrars
return (hints, beanFactory) -> {
// Register @PreAuthorize/@PostAuthorize hints
PrePostAuthorizeHintsRegistrar prePostRegistrar =
new PrePostAuthorizeHintsRegistrar();
prePostRegistrar.registerHints(hints, beanFactory);
// Register expression bean hints
PrePostAuthorizeExpressionBeanHintsRegistrar expressionRegistrar =
new PrePostAuthorizeExpressionBeanHintsRegistrar(
PermissionService.class,
ValidationService.class
);
expressionRegistrar.registerHints(hints, beanFactory);
// Register authorization proxy hints
AuthorizeReturnObjectCoreHintsRegistrar proxyRegistrar =
new AuthorizeReturnObjectCoreHintsRegistrar(proxyFactory);
proxyRegistrar.registerHints(hints, beanFactory);
};
}
}@Configuration
@EnableWebSecurity
@EnableMethodSecurity
public class CompleteNativeSecurityConfig {
@Bean
public RuntimeHintsRegistrar completeSecurityHints(
AuthorizationProxyFactory proxyFactory) {
return (hints, classLoader) -> {
// 1. Core authentication types
registerAuthenticationTypes(hints);
// 2. Custom user details
registerUserDetailsTypes(hints);
// 3. Security expressions
registerExpressionTypes(hints);
// 4. Resources
registerResources(hints);
// 5. Proxies
registerProxies(hints);
// 6. Serialization
registerSerialization(hints);
};
}
private void registerAuthenticationTypes(RuntimeHints hints) {
hints.reflection()
.registerType(CustomAuthenticationToken.class,
builder -> builder
.withMembers(MemberCategory.INVOKE_PUBLIC_CONSTRUCTORS)
.withMembers(MemberCategory.INVOKE_PUBLIC_METHODS)
.withMembers(MemberCategory.DECLARED_FIELDS)
);
}
private void registerUserDetailsTypes(RuntimeHints hints) {
hints.reflection()
.registerType(CustomUserDetails.class,
builder -> builder
.withMembers(MemberCategory.INVOKE_PUBLIC_CONSTRUCTORS)
.withMembers(MemberCategory.INVOKE_PUBLIC_METHODS)
);
}
private void registerExpressionTypes(RuntimeHints hints) {
hints.reflection()
.registerType(CustomSecurityExpressionRoot.class,
builder -> builder
.withMembers(MemberCategory.INVOKE_PUBLIC_METHODS)
);
}
private void registerResources(RuntimeHints hints) {
hints.resources()
.registerPattern("security/*.properties")
.registerPattern("security/*.xml");
}
private void registerProxies(RuntimeHints hints) {
hints.proxies()
.registerJdkProxy(AuthenticationProvider.class)
.registerJdkProxy(UserDetailsService.class);
}
private void registerSerialization(RuntimeHints hints) {
hints.serialization()
.registerType(CustomAuthenticationToken.class)
.registerType(CustomUserDetails.class);
}
@Bean
public SecurityHintsRegistrar authorizationProxyHints(
AuthorizationProxyFactory proxyFactory) {
return new AuthorizeReturnObjectCoreHintsRegistrar(proxyFactory);
}
}Test native image compilation:
@SpringBootTest
@EnabledInNativeImage
class NativeSecurityApplicationTests {
@Autowired
private AuthenticationManager authenticationManager;
@Test
void authenticationWorks() {
UsernamePasswordAuthenticationToken token =
UsernamePasswordAuthenticationToken.unauthenticated(
"user",
"password"
);
Authentication result = authenticationManager.authenticate(token);
assertThat(result.isAuthenticated()).isTrue();
}
@Test
void methodSecurityWorks() {
// Test method security in native image
}
}# Maven
./mvnw -Pnative native:compile
# Gradle
./gradlew nativeCompile
# Run native image
./target/native-security-application
# Or with Gradle
./build/native/nativeCompile/native-security-applicationProblem: ClassNotFoundException or NoSuchMethodException at runtime.
Solution: Register missing types in RuntimeHints:
@Bean
public RuntimeHintsRegistrar reflectionHints() {
return (hints, classLoader) -> {
hints.reflection()
.registerType(MissingClass.class,
MemberCategory.INVOKE_PUBLIC_CONSTRUCTORS,
MemberCategory.INVOKE_PUBLIC_METHODS,
MemberCategory.DECLARED_FIELDS
);
};
}Problem: Resources not found in native image.
Solution: Register resource patterns:
hints.resources()
.registerPattern("path/to/resource.properties")
.registerPattern("templates/*.html");Problem: Dynamic proxy errors at runtime.
Solution: Register proxy interfaces:
hints.proxies()
.registerJdkProxy(InterfaceA.class, InterfaceB.class);Problem: Serialization fails in native image.
Solution: Register serializable types:
hints.serialization()
.registerType(SerializableClass.class);Native Image Benefits:
Trade-offs:
Typical Metrics:
Traditional JVM:
- Startup: 2-5 seconds
- Memory: 200-500 MB
Native Image:
- Startup: 50-200 milliseconds
- Memory: 50-100 MBSpring Security's AOT processor automatically:
No additional configuration required for standard Spring Security features.
# Native build with detailed logging
./mvnw -Pnative native:compile \
-Dverbose \
-H:+ReportExceptionStackTraces \
-H:+PrintClassInitialization# Generate AOT hints without building
./mvnw spring-boot:process-aot
# Inspect generated files
ls target/spring-aot/main/# Build with debug info
native-image \
--debug-attach \
-H:+ReportExceptionStackTraces \
-jar application.jar