Core authentication and authorization framework for Spring applications with comprehensive user management and security context handling
Spring Security provides comprehensive support for Java Authentication and Authorization Service (JAAS) integration. This allows applications to leverage existing JAAS LoginModules while benefiting from Spring Security's architecture and features.
Core Capabilities:
JaasAuthenticationProvider and DefaultJaasAuthenticationProviderLoginContext for authenticationAuthorityGranter (maps JAAS principals to Spring Security authorities)JaasAuthenticationCallbackHandler for flexible authenticationLoginExceptionResolver (JAAS exceptions to Spring Security exceptions)Key Interfaces and Classes:
AbstractJaasAuthenticationProvider - Abstract base with JAAS integrationJaasAuthenticationProvider - Concrete implementation with explicit configurationDefaultJaasAuthenticationProvider - Implementation with callback handler supportJaasAuthenticationToken - Authentication token containing LoginContextAuthorityGranter - Interface: grant(Principal) returns Set of authority stringsJaasAuthenticationCallbackHandler - Interface for handling JAAS callbacksLoginExceptionResolver - Interface: resolveException(LoginException) returns AuthenticationExceptionInMemoryConfiguration - In-memory JAAS configuration (for testing)Default Behaviors:
UsernamePasswordAuthenticationToken (via supports() method)DefaultLoginExceptionResolver (maps common exceptions)JaasNameCallbackHandler, JaasPasswordCallbackHandler (extract from Authentication)Threading Model:
Lifecycle:
setConfiguration() (required)setAuthorityGranters() (required)setLoginContextName() (required)setLoginExceptionResolver() (optional)Exceptions:
LoginException - JAAS exception (mapped to Spring Security exception via resolver)BadCredentialsException - Mapped from FailedLoginExceptionAccountExpiredException - Mapped from JAAS AccountExpiredExceptionLockedException - Mapped from AccountLockedExceptionCredentialsExpiredException - Mapped from CredentialExpiredExceptionEdge Cases:
JaasAuthenticationCallbackHandler for custom callback typesSecurityContextLoginModule in JAAS configurationInMemoryConfiguration or external JAAS config fileAbstract base class for JAAS-based authentication providers.
public abstract class AbstractJaasAuthenticationProvider
implements AuthenticationProvider, ApplicationEventPublisherAware { .api }Description: Base provider that delegates authentication to JAAS LoginContext.
Abstract Methods:
protected abstract Configuration getConfiguration() { .api }protected abstract AuthorityGranter[] getAuthorityGranters() { .api }Methods:
public Authentication authenticate(Authentication auth)
throws AuthenticationException { .api }JaasAuthenticationToken on successpublic boolean supports(Class<?> aClass) { .api }UsernamePasswordAuthenticationTokenpublic void setLoginContextName(String loginContextName) { .api }loginContextName - The name in JAAS configurationpublic void setLoginExceptionResolver(
LoginExceptionResolver loginExceptionResolver) { .api }loginExceptionResolver - Custom exception resolverpublic void setApplicationEventPublisher(
ApplicationEventPublisher applicationEventPublisher) { .api }Package: org.springframework.security.authentication.jaas
Concrete implementation using explicit Configuration and AuthorityGranters.
public class JaasAuthenticationProvider
extends AbstractJaasAuthenticationProvider { .api }Description: JAAS provider with explicit configuration and authority mapping.
Constructor:
public JaasAuthenticationProvider() { .api }Methods:
public void setConfiguration(Configuration configuration) { .api }configuration - JAAS Configuration instancepublic void setAuthorityGranters(AuthorityGranter[] authorityGranters) { .api }authorityGranters - Array of AuthorityGranter implementationsExample:
@Bean
public JaasAuthenticationProvider jaasAuthenticationProvider() {
JaasAuthenticationProvider provider = new JaasAuthenticationProvider();
provider.setConfiguration(jaasConfiguration());
provider.setAuthorityGranters(new AuthorityGranter[] {
new SampleAuthorityGranter()
});
provider.setLoginContextName("Sample");
return provider;
}
@Bean
public Configuration jaasConfiguration() {
InMemoryConfiguration config = new InMemoryConfiguration();
Map<String, AppConfigurationEntry[]> mappings = new HashMap<>();
AppConfigurationEntry entry = new AppConfigurationEntry(
SampleLoginModule.class.getName(),
AppConfigurationEntry.LoginModuleControlFlag.REQUIRED,
new HashMap<>()
);
mappings.put("Sample", new AppConfigurationEntry[] { entry });
config.setMappings(mappings);
return config;
}Provider implementation using callback handlers for flexible authentication.
public class DefaultJaasAuthenticationProvider
extends AbstractJaasAuthenticationProvider { .api }Description: JAAS provider supporting custom callback handlers.
Methods:
public void setCallbackHandlers(
JaasAuthenticationCallbackHandler[] callbackHandlers) { .api }callbackHandlers - Array of callback handler implementationsExample:
@Bean
public DefaultJaasAuthenticationProvider defaultJaasProvider() {
DefaultJaasAuthenticationProvider provider =
new DefaultJaasAuthenticationProvider();
provider.setConfiguration(jaasConfiguration());
provider.setLoginContextName("DefaultLogin");
provider.setCallbackHandlers(new JaasAuthenticationCallbackHandler[] {
new JaasNameCallbackHandler(),
new JaasPasswordCallbackHandler()
});
provider.setAuthorityGranters(new AuthorityGranter[] {
new TestAuthorityGranter()
});
return provider;
}Authentication token representing successful JAAS authentication.
public class JaasAuthenticationToken
extends UsernamePasswordAuthenticationToken { .api }Description: Token containing JAAS LoginContext and authenticated principals.
Constructor:
public JaasAuthenticationToken(
Object principal,
Object credentials,
Collection<? extends GrantedAuthority> authorities,
LoginContext loginContext) { .api }Parameters:
principal - The authenticated principalcredentials - The credentialsauthorities - The granted authoritiesloginContext - The JAAS LoginContextMethods:
public LoginContext getLoginContext() { .api }Example:
public void processAuthentication(Authentication auth) {
if (auth instanceof JaasAuthenticationToken) {
JaasAuthenticationToken jaasToken = (JaasAuthenticationToken) auth;
LoginContext loginContext = jaasToken.getLoginContext();
// Access JAAS Subject
Subject subject = loginContext.getSubject();
Set<Principal> principals = subject.getPrincipals();
// Process JAAS principals
for (Principal principal : principals) {
System.out.println("JAAS Principal: " + principal.getName());
}
}
}Interface for mapping JAAS principals to Spring Security authorities.
public interface AuthorityGranter { .api }Description: Strategy for granting authorities based on JAAS principals.
Methods:
Set<String> grant(Principal principal) { .api }principal - JAAS Principal from authenticated subjectExample:
public class CustomAuthorityGranter implements AuthorityGranter {
@Override
public Set<String> grant(Principal principal) {
Set<String> authorities = new HashSet<>();
// Map specific principal types to roles
if (principal instanceof AdminPrincipal) {
authorities.add("ROLE_ADMIN");
authorities.add("ROLE_USER");
} else if (principal instanceof UserPrincipal) {
authorities.add("ROLE_USER");
}
// Map based on principal name
String name = principal.getName();
if (name.startsWith("admin_")) {
authorities.add("ROLE_ADMIN");
}
return authorities;
}
}GrantedAuthority implementation that wraps a JAAS Principal.
public class JaasGrantedAuthority implements GrantedAuthority { .api }Description: Authority representing a JAAS Principal.
Constructor:
public JaasGrantedAuthority(String role, Principal principal) { .api }Parameters:
role - The authority/role nameprincipal - The underlying JAAS PrincipalMethods:
public String getAuthority() { .api }public Principal getPrincipal() { .api }Example:
public List<GrantedAuthority> mapPrincipals(Set<Principal> principals) {
List<GrantedAuthority> authorities = new ArrayList<>();
for (Principal principal : principals) {
authorities.add(
new JaasGrantedAuthority("ROLE_" + principal.getName(), principal)
);
}
return authorities;
}Interface for handling JAAS Callbacks during authentication.
public interface JaasAuthenticationCallbackHandler { .api }Description: Handles specific JAAS Callback types.
Methods:
void handle(Callback callback, Authentication authentication)
throws IOException, UnsupportedCallbackException { .api }callback - The JAAS Callback to handleauthentication - The Spring Security Authentication containing credentialsHandles NameCallback by extracting principal from Authentication.
public class JaasNameCallbackHandler
implements JaasAuthenticationCallbackHandler { .api }Description: Populates NameCallback with username from authentication.
Example:
public class CustomCallbackHandler
implements JaasAuthenticationCallbackHandler {
@Override
public void handle(Callback callback, Authentication authentication)
throws IOException, UnsupportedCallbackException {
if (callback instanceof NameCallback) {
NameCallback nameCallback = (NameCallback) callback;
nameCallback.setName(authentication.getName());
} else {
throw new UnsupportedCallbackException(callback);
}
}
}Handles PasswordCallback by extracting credentials from Authentication.
public class JaasPasswordCallbackHandler
implements JaasAuthenticationCallbackHandler { .api }Description: Populates PasswordCallback with password from authentication.
JAAS LoginModule that populates SecurityContext with authenticated subject.
public class SecurityContextLoginModule implements LoginModule { .api }Description: LoginModule that integrates JAAS authentication with Spring Security's SecurityContext.
Methods:
public void initialize(Subject subject, CallbackHandler callbackHandler,
Map<String, ?> sharedState, Map<String, ?> options) { .api }public boolean login() throws LoginException { .api }public boolean commit() throws LoginException { .api }public boolean abort() throws LoginException { .api }public boolean logout() throws LoginException { .api }Configuration:
<!-- JAAS configuration file -->
<Sample {
com.example.CustomLoginModule required;
org.springframework.security.authentication.jaas.SecurityContextLoginModule required;
};>Example:
// Use in JAAS configuration
public Configuration createConfiguration() {
AppConfigurationEntry securityContextEntry =
new AppConfigurationEntry(
SecurityContextLoginModule.class.getName(),
AppConfigurationEntry.LoginModuleControlFlag.REQUIRED,
Collections.emptyMap()
);
AppConfigurationEntry customEntry =
new AppConfigurationEntry(
CustomLoginModule.class.getName(),
AppConfigurationEntry.LoginModuleControlFlag.REQUIRED,
Collections.emptyMap()
);
InMemoryConfiguration config = new InMemoryConfiguration();
config.setMappings(Map.of("MyApp",
new AppConfigurationEntry[] { customEntry, securityContextEntry }
));
return config;
}Interface for resolving JAAS LoginExceptions to Spring Security exceptions.
public interface LoginExceptionResolver { .api }Description: Strategy for mapping JAAS exceptions to Spring Security exceptions.
Methods:
AuthenticationException resolveException(LoginException exception) { .api }exception - JAAS LoginExceptionDefault implementation mapping common JAAS exceptions.
public class DefaultLoginExceptionResolver
implements LoginExceptionResolver { .api }Description: Maps standard JAAS exceptions to Spring Security exceptions.
Mappings:
FailedLoginException → BadCredentialsExceptionAccountExpiredException → AccountExpiredExceptionCredentialExpiredException → CredentialsExpiredExceptionAccountLockedException → LockedExceptionExample:
public class CustomLoginExceptionResolver
implements LoginExceptionResolver {
@Override
public AuthenticationException resolveException(
LoginException exception) {
if (exception instanceof FailedLoginException) {
return new BadCredentialsException(
"Invalid credentials", exception);
} else if (exception instanceof AccountExpiredException) {
return new org.springframework.security.authentication
.AccountExpiredException(
"Account expired", exception);
}
// Default fallback
return new AuthenticationServiceException(
"Login failed", exception);
}
}JAAS Configuration implementation storing configuration in memory.
public class InMemoryConfiguration extends Configuration { .api }Description: In-memory JAAS configuration for testing and simple scenarios.
Methods:
public AppConfigurationEntry[] getAppConfigurationEntry(String name) { .api }public void setMappings(
Map<String, AppConfigurationEntry[]> mappings) { .api }mappings - Map of application name to configuration entriesPackage: org.springframework.security.authentication.jaas.memory
Example:
@Bean
public Configuration jaasConfiguration() {
InMemoryConfiguration config = new InMemoryConfiguration();
Map<String, Object> options = new HashMap<>();
options.put("debug", "true");
AppConfigurationEntry entry = new AppConfigurationEntry(
"com.example.MyLoginModule",
AppConfigurationEntry.LoginModuleControlFlag.REQUIRED,
options
);
Map<String, AppConfigurationEntry[]> mappings = new HashMap<>();
mappings.put("MyApplication", new AppConfigurationEntry[] { entry });
config.setMappings(mappings);
return config;
}Base class for JAAS authentication events.
public abstract class JaasAuthenticationEvent extends ApplicationEvent { .api }Methods:
public JaasAuthenticationToken getAuthenticationToken() { .api }Package: org.springframework.security.authentication.jaas.event
Published when JAAS authentication succeeds.
public class JaasAuthenticationSuccessEvent extends JaasAuthenticationEvent { .api }Constructor:
public JaasAuthenticationSuccessEvent(JaasAuthenticationToken token) { .api }Published when JAAS authentication fails.
public class JaasAuthenticationFailedEvent extends JaasAuthenticationEvent { .api }Constructor:
public JaasAuthenticationFailedEvent(
JaasAuthenticationToken token,
AuthenticationException exception) { .api }Methods:
public AuthenticationException getException() { .api }Example:
@Component
public class JaasEventListener {
@EventListener
public void onJaasSuccess(JaasAuthenticationSuccessEvent event) {
JaasAuthenticationToken token = event.getAuthenticationToken();
LoginContext loginContext = token.getLoginContext();
Subject subject = loginContext.getSubject();
System.out.println("JAAS authentication successful");
System.out.println("Subject principals: " +
subject.getPrincipals().size());
}
@EventListener
public void onJaasFailure(JaasAuthenticationFailedEvent event) {
AuthenticationException exception = event.getException();
System.err.println("JAAS authentication failed: " +
exception.getMessage());
}
}@Configuration
public class JaasSecurityConfig {
@Bean
public JaasAuthenticationProvider jaasAuthenticationProvider() {
JaasAuthenticationProvider provider = new JaasAuthenticationProvider();
provider.setConfiguration(jaasConfiguration());
provider.setLoginContextName("MyApplication");
provider.setAuthorityGranters(authorityGranters());
provider.setLoginExceptionResolver(new DefaultLoginExceptionResolver());
return provider;
}
@Bean
public Configuration jaasConfiguration() {
InMemoryConfiguration config = new InMemoryConfiguration();
Map<String, Object> options = Map.of(
"debug", "false",
"useTicketCache", "true"
);
AppConfigurationEntry loginModule = new AppConfigurationEntry(
CustomLoginModule.class.getName(),
AppConfigurationEntry.LoginModuleControlFlag.REQUIRED,
options
);
AppConfigurationEntry securityContextModule =
new AppConfigurationEntry(
SecurityContextLoginModule.class.getName(),
AppConfigurationEntry.LoginModuleControlFlag.REQUIRED,
Collections.emptyMap()
);
config.setMappings(Map.of("MyApplication",
new AppConfigurationEntry[] { loginModule, securityContextModule }
));
return config;
}
@Bean
public AuthorityGranter[] authorityGranters() {
return new AuthorityGranter[] {
new AdminAuthorityGranter(),
new UserAuthorityGranter()
};
}
@Bean
public AuthenticationManager authenticationManager() {
return new ProviderManager(
Collections.singletonList(jaasAuthenticationProvider())
);
}
}
// Custom authority granter
public class AdminAuthorityGranter implements AuthorityGranter {
@Override
public Set<String> grant(Principal principal) {
if (principal.getName().contains("admin")) {
return Set.of("ROLE_ADMIN", "ROLE_USER");
}
return Collections.emptySet();
}
}
// Custom login module
public class CustomLoginModule implements LoginModule {
private Subject subject;
private CallbackHandler callbackHandler;
private boolean succeeded = false;
private Principal userPrincipal;
@Override
public void initialize(Subject subject, CallbackHandler callbackHandler,
Map<String, ?> sharedState, Map<String, ?> options) {
this.subject = subject;
this.callbackHandler = callbackHandler;
}
@Override
public boolean login() throws LoginException {
NameCallback nameCallback = new NameCallback("Username: ");
PasswordCallback passwordCallback =
new PasswordCallback("Password: ", false);
try {
callbackHandler.handle(
new Callback[] { nameCallback, passwordCallback }
);
String username = nameCallback.getName();
char[] password = passwordCallback.getPassword();
// Perform authentication
if (authenticate(username, password)) {
userPrincipal = new UserPrincipal(username);
succeeded = true;
return true;
}
throw new FailedLoginException("Authentication failed");
} catch (IOException | UnsupportedCallbackException e) {
throw new LoginException(e.getMessage());
}
}
@Override
public boolean commit() throws LoginException {
if (succeeded) {
subject.getPrincipals().add(userPrincipal);
return true;
}
return false;
}
@Override
public boolean abort() throws LoginException {
succeeded = false;
userPrincipal = null;
return true;
}
@Override
public boolean logout() throws LoginException {
subject.getPrincipals().remove(userPrincipal);
succeeded = false;
userPrincipal = null;
return true;
}
private boolean authenticate(String username, char[] password) {
// Implement authentication logic
return true;
}
}Install with Tessl CLI
npx tessl i tessl/maven-spring-security-coredocs