Configure authentication mechanisms and user storage through AuthenticationManagerBuilder and related configurers. Supports in-memory, JDBC, LDAP, and custom authentication providers with thread-safe runtime behavior.
Builder for configuring AuthenticationManager. Not thread-safe during configuration. Thread-safe after build() is called.
package org.springframework.security.config.annotation.authentication.builders;
/**
* Builder for configuring AuthenticationManager.
* Not thread-safe during configuration. Thread-safe after build().
*
* @since 3.2
*/
public class AuthenticationManagerBuilder
extends AbstractConfiguredSecurityBuilder<AuthenticationManager, AuthenticationManagerBuilder>
implements ProviderManagerBuilder<AuthenticationManagerBuilder> {
/**
* Configures in-memory user store.
* Useful for testing or simple applications.
*
* @return configurer for in-memory authentication (never null)
* @throws Exception if configuration fails
*/
public InMemoryUserDetailsManagerConfigurer<AuthenticationManagerBuilder>
inMemoryAuthentication() throws Exception;
/**
* Configures JDBC-based user store.
* Requires DataSource bean or explicit dataSource() call.
*
* @return configurer for JDBC authentication (never null)
* @throws Exception if configuration fails
*/
public JdbcUserDetailsManagerConfigurer<AuthenticationManagerBuilder>
jdbcAuthentication() throws Exception;
/**
* Configures custom UserDetailsService.
* UserDetailsService must be thread-safe for concurrent access.
*
* @param userDetailsService the service to use (must not be null)
* @return configurer for UserDetailsService authentication (never null)
* @throws Exception if configuration fails
* @throws IllegalArgumentException if userDetailsService is null
*/
public <T extends UserDetailsService> DaoAuthenticationConfigurer<AuthenticationManagerBuilder, T>
userDetailsService(T userDetailsService) throws Exception;
/**
* Configures LDAP authentication.
* Requires BaseLdapPathContextSource or explicit contextSource() call.
*
* @return configurer for LDAP authentication (never null)
* @throws Exception if configuration fails
*/
public LdapAuthenticationProviderConfigurer<AuthenticationManagerBuilder>
ldapAuthentication() throws Exception;
/**
* Adds custom AuthenticationProvider.
* AuthenticationProvider must be thread-safe for concurrent use.
* Multiple providers can be added - they are tried in order.
*
* @param authenticationProvider the provider to add (must not be null)
* @return this builder for method chaining
* @throws IllegalArgumentException if authenticationProvider is null
*/
public AuthenticationManagerBuilder authenticationProvider(
AuthenticationProvider authenticationProvider
);
/**
* Sets parent AuthenticationManager for fallback.
* If no provider can authenticate, parent is consulted.
*
* @param authenticationManager the parent manager (must not be null)
* @return this builder for method chaining
* @throws IllegalArgumentException if authenticationManager is null
*/
public AuthenticationManagerBuilder parentAuthenticationManager(
AuthenticationManager authenticationManager
);
/**
* Configures whether to erase credentials after authentication.
* Default: true (credentials are erased for security)
*
* @param eraseCredentials true to erase credentials after authentication
* @return this builder for method chaining
*/
public AuthenticationManagerBuilder eraseCredentials(boolean eraseCredentials);
/**
* Builds the AuthenticationManager.
* Must be called to finalize configuration.
*
* @return configured AuthenticationManager (never null)
* @throws Exception if configuration is invalid or incomplete
* @throws IllegalStateException if no authentication provider configured
*/
public AuthenticationManager build() throws Exception;
}Usage Pattern:
@Bean
public AuthenticationManager authenticationManager(
HttpSecurity http,
UserDetailsService userDetailsService,
PasswordEncoder passwordEncoder) throws Exception {
AuthenticationManagerBuilder authBuilder =
http.getSharedObject(AuthenticationManagerBuilder.class);
authBuilder
.userDetailsService(userDetailsService)
.passwordEncoder(passwordEncoder);
return authBuilder.build();
}Thread Safety:
AuthenticationManager is thread-safe for concurrent authentication requests@Bean methods (single-threaded during startup)Configure in-memory user store for testing or simple applications. Thread-safe for concurrent access.
package org.springframework.security.config.annotation.authentication.configurers.provisioning;
/**
* Configurer for in-memory user authentication.
* Thread-safe: InMemoryUserDetailsManager is thread-safe.
*
* @param <B> the builder type
* @since 3.2
*/
public class InMemoryUserDetailsManagerConfigurer<B extends ProviderManagerBuilder<B>>
extends UserDetailsManagerConfigurer<B, InMemoryUserDetailsManagerConfigurer<B>> {
/**
* Adds a user with the specified username.
* Returns builder for configuring user details.
*
* @param username the username (must not be null or empty)
* @return builder for configuring user details (never null)
* @throws IllegalArgumentException if username is null or empty
*/
public UserDetailsBuilder withUser(String username);
/**
* Adds a pre-configured UserDetails object.
*
* @param userDetails the user details (must not be null)
* @return this configurer for method chaining
* @throws IllegalArgumentException if userDetails is null
*/
public InMemoryUserDetailsManagerConfigurer<B> withUser(UserDetails userDetails);
/**
* Builder for configuring user details.
* Not thread-safe - use during configuration only.
*/
public final class UserDetailsBuilder {
/**
* Sets the password.
* Use password prefix format: {noop}password, {bcrypt}..., etc.
*
* @param password the password (must not be null)
* @return this builder for method chaining
* @throws IllegalArgumentException if password is null
*/
public UserDetailsBuilder password(String password);
/**
* Sets roles (automatically prefixed with ROLE_).
*
* @param roles the roles (must not be null)
* @return this builder for method chaining
*/
public UserDetailsBuilder roles(String... roles);
/**
* Sets authorities (no automatic prefix).
*
* @param authorities the authorities (must not be null)
* @return this builder for method chaining
*/
public UserDetailsBuilder authorities(String... authorities);
/**
* Sets authorities as GrantedAuthority objects.
*
* @param authorities the authorities (must not be null)
* @return this builder for method chaining
*/
public UserDetailsBuilder authorities(GrantedAuthority... authorities);
/**
* Sets whether account is expired.
*
* @param accountExpired true if account is expired
* @return this builder for method chaining
*/
public UserDetailsBuilder accountExpired(boolean accountExpired);
/**
* Sets whether account is locked.
*
* @param accountLocked true if account is locked
* @return this builder for method chaining
*/
public UserDetailsBuilder accountLocked(boolean accountLocked);
/**
* Sets whether credentials are expired.
*
* @param credentialsExpired true if credentials are expired
* @return this builder for method chaining
*/
public UserDetailsBuilder credentialsExpired(boolean credentialsExpired);
/**
* Sets whether account is disabled.
*
* @param disabled true if account is disabled
* @return this builder for method chaining
*/
public UserDetailsBuilder disabled(boolean disabled);
/**
* Completes user configuration and returns to configurer.
*
* @return the configurer for adding more users or finalizing
*/
public InMemoryUserDetailsManagerConfigurer<B> and();
}
}Usage Examples:
// Recommended: Configure via UserDetailsService bean
@Bean
public UserDetailsService userDetailsService() {
UserDetails user = User.withUsername("user")
.password("{noop}password") // {noop} = no encoding (testing only)
.roles("USER")
.build();
UserDetails admin = User.withUsername("admin")
.password("{bcrypt}$2a$10$GRLdNijSQMUvl/au9ofL.eDwmoohzzS7.rmNSJZ.0FxO/BTk76klW")
.roles("USER", "ADMIN")
.build();
return new InMemoryUserDetailsManager(user, admin);
}
// Using AuthenticationManagerBuilder (less common)
@Bean
public AuthenticationManager authenticationManager(HttpSecurity http) throws Exception {
AuthenticationManagerBuilder authBuilder =
http.getSharedObject(AuthenticationManagerBuilder.class);
authBuilder
.inMemoryAuthentication()
.withUser("user")
.password("{noop}password")
.roles("USER")
.and()
.withUser("admin")
.password("{noop}admin")
.roles("USER", "ADMIN")
.accountExpired(false)
.accountLocked(false)
.credentialsExpired(false)
.disabled(false);
return authBuilder.build();
}
// With password encoder
@Bean
public UserDetailsService userDetailsService(PasswordEncoder encoder) {
return new InMemoryUserDetailsManager(
User.withUsername("user")
.password(encoder.encode("password"))
.roles("USER")
.build()
);
}Important Notes:
{noop}, {bcrypt}, {pbkdf2}, {scrypt}, {sha256}{noop} means no encoding - never use in productionThread Safety:
InMemoryUserDetailsManager is thread-safe for concurrent accessPerformance:
Configure JDBC-based user store with database. Thread-safe if DataSource and queries are thread-safe.
package org.springframework.security.config.annotation.authentication.configurers.provisioning;
/**
* Configurer for JDBC-based user authentication.
* Thread-safe: JdbcUserDetailsManager is thread-safe if DataSource is thread-safe.
*
* @param <B> the builder type
* @since 3.2
*/
public class JdbcUserDetailsManagerConfigurer<B extends ProviderManagerBuilder<B>>
extends UserDetailsManagerConfigurer<B, JdbcUserDetailsManagerConfigurer<B>> {
/**
* Sets the DataSource for database access.
*
* @param dataSource the data source (must not be null)
* @return this configurer for method chaining
* @throws IllegalArgumentException if dataSource is null
*/
public JdbcUserDetailsManagerConfigurer<B> dataSource(DataSource dataSource);
/**
* Sets custom query for loading users.
* Query must return: username, password, enabled (in that order).
*
* @param query the SQL query (must not be null)
* @return this configurer for method chaining
* @throws IllegalArgumentException if query is null
*/
public JdbcUserDetailsManagerConfigurer<B> usersByUsernameQuery(String query);
/**
* Sets custom query for loading authorities.
* Query must return: username, authority (in that order).
*
* @param query the SQL query (must not be null)
* @return this configurer for method chaining
* @throws IllegalArgumentException if query is null
*/
public JdbcUserDetailsManagerConfigurer<B> authoritiesByUsernameQuery(String query);
/**
* Sets custom query for loading group authorities.
* Query must return: id, group_name, authority (in that order).
*
* @param query the SQL query (must not be null)
* @return this configurer for method chaining
*/
public JdbcUserDetailsManagerConfigurer<B> groupAuthoritiesByUsername(String query);
/**
* Sets the role prefix (default: "ROLE_").
*
* @param rolePrefix the role prefix (must not be null)
* @return this configurer for method chaining
*/
public JdbcUserDetailsManagerConfigurer<B> rolePrefix(String rolePrefix);
/**
* Sets the password encoder.
* Required if passwords are encoded in database.
*
* @param passwordEncoder the encoder (must not be null)
* @return this configurer for method chaining
* @throws IllegalArgumentException if passwordEncoder is null
*/
public JdbcUserDetailsManagerConfigurer<B> passwordEncoder(PasswordEncoder passwordEncoder);
/**
* Uses default Spring Security schema.
* Creates users and authorities tables if they don't exist.
*
* @return this configurer for method chaining
*/
public JdbcUserDetailsManagerConfigurer<B> withDefaultSchema();
}Usage Examples:
// Recommended: Configure via UserDetailsManager bean
@Bean
public UserDetailsManager userDetailsManager(DataSource dataSource) {
JdbcUserDetailsManager manager = new JdbcUserDetailsManager(dataSource);
// Uses default schema and queries
return manager;
}
// Custom queries
@Bean
public UserDetailsManager customJdbcUserDetailsManager(
DataSource dataSource,
PasswordEncoder encoder) {
JdbcUserDetailsManager manager = new JdbcUserDetailsManager(dataSource);
manager.setUsersByUsernameQuery(
"SELECT username, password, enabled FROM custom_users WHERE username = ?"
);
manager.setAuthoritiesByUsernameQuery(
"SELECT username, authority FROM custom_authorities WHERE username = ?"
);
manager.setPasswordEncoder(encoder);
return manager;
}
// Using AuthenticationManagerBuilder
@Bean
public AuthenticationManager authenticationManager(
HttpSecurity http,
DataSource dataSource,
PasswordEncoder encoder) throws Exception {
AuthenticationManagerBuilder authBuilder =
http.getSharedObject(AuthenticationManagerBuilder.class);
authBuilder
.jdbcAuthentication()
.dataSource(dataSource)
.passwordEncoder(encoder)
.usersByUsernameQuery("SELECT username, password, enabled FROM users WHERE username = ?")
.authoritiesByUsernameQuery("SELECT username, authority FROM authorities WHERE username = ?");
return authBuilder.build();
}
// Default schema (for withDefaultSchema())
/*
CREATE TABLE users (
username VARCHAR(50) NOT NULL PRIMARY KEY,
password VARCHAR(500) NOT NULL,
enabled BOOLEAN NOT NULL
);
CREATE TABLE authorities (
username VARCHAR(50) NOT NULL,
authority VARCHAR(50) NOT NULL,
FOREIGN KEY (username) REFERENCES users(username)
);
CREATE UNIQUE INDEX ix_auth_username ON authorities (username, authority);
*/Important Notes:
Thread Safety:
JdbcUserDetailsManager is thread-safe if DataSource is thread-safePerformance:
Configure authentication with custom UserDetailsService. UserDetailsService must be thread-safe.
package org.springframework.security.config.annotation.authentication.configurers.userdetails;
/**
* Configurer for UserDetailsService-based authentication.
* Thread-safe: Depends on UserDetailsService thread safety.
*
* @param <B> the builder type
* @param <U> the UserDetailsService type
* @since 3.2
*/
public class DaoAuthenticationConfigurer<B extends ProviderManagerBuilder<B>, U extends UserDetailsService>
extends AbstractDaoAuthenticationConfigurer<B, DaoAuthenticationConfigurer<B, U>, U> {
/**
* Sets the UserDetailsService.
* UserDetailsService must be thread-safe for concurrent access.
*
* @param userDetailsService the service (must not be null)
* @return this configurer for method chaining
* @throws IllegalArgumentException if userDetailsService is null
*/
public DaoAuthenticationConfigurer<B, U> userDetailsService(U userDetailsService);
/**
* Sets the password encoder.
* Required if passwords are encoded.
*
* @param passwordEncoder the encoder (must not be null)
* @return this configurer for method chaining
* @throws IllegalArgumentException if passwordEncoder is null
*/
public DaoAuthenticationConfigurer<B, U> passwordEncoder(PasswordEncoder passwordEncoder);
/**
* Sets the password manager for password updates.
*
* @param passwordManager the manager (must not be null)
* @return this configurer for method chaining
*/
public DaoAuthenticationConfigurer<B, U> userDetailsPasswordManager(
UserDetailsPasswordService passwordManager
);
}Usage Example:
@Service
public class CustomUserDetailsService implements UserDetailsService {
@Autowired
private UserRepository userRepository;
@Override
public UserDetails loadUserByUsername(String username)
throws UsernameNotFoundException {
// Must be thread-safe for concurrent access
com.example.User user = userRepository.findByUsername(username)
.orElseThrow(() -> new UsernameNotFoundException("User not found: " + username));
return User.withUsername(user.getUsername())
.password(user.getPassword()) // Should be encoded
.authorities(user.getAuthorities())
.accountExpired(!user.isAccountNonExpired())
.accountLocked(!user.isAccountNonLocked())
.credentialsExpired(!user.isCredentialsNonExpired())
.disabled(!user.isEnabled())
.build();
}
}
// Configuration
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Autowired
private CustomUserDetailsService userDetailsService;
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.userDetailsService(userDetailsService) // Sets UserDetailsService
.authorizeHttpRequests(authorize -> authorize
.anyRequest().authenticated()
)
.formLogin(Customizer.withDefaults());
return http.build();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}Important Notes:
UserDetailsService must be thread-safeloadUserByUsername() is called for every authentication attemptUsernameNotFoundException if user not foundUserDetails objectPasswordEncoderThread Safety:
UserDetailsService implementation must be thread-safeloadUserByUsername() may be called concurrentlyPerformance:
CachingUserDetailsService wrapperConfigure LDAP authentication. Thread-safe if LDAP context source is thread-safe.
package org.springframework.security.config.annotation.authentication.configurers.ldap;
/**
* Configurer for LDAP authentication.
* Thread-safe: Depends on BaseLdapPathContextSource thread safety.
*
* @param <B> the builder type
* @since 3.2
*/
public class LdapAuthenticationProviderConfigurer<B extends ProviderManagerBuilder<B>>
extends AbstractDaoAuthenticationConfigurer<B, LdapAuthenticationProviderConfigurer<B>,
UserDetailsService> {
/**
* Sets user DN patterns for direct bind.
* Pattern {0} is replaced with username.
* Example: "uid={0},ou=people"
*
* @param userDnPatterns the patterns (must not be null)
* @return this configurer for method chaining
*/
public LdapAuthenticationProviderConfigurer<B> userDnPatterns(String... userDnPatterns);
/**
* Sets base DN for user search.
*
* @param userSearchBase the base DN (must not be null)
* @return this configurer for method chaining
*/
public LdapAuthenticationProviderConfigurer<B> userSearchBase(String userSearchBase);
/**
* Sets LDAP filter for user search.
* Filter {0} is replaced with username.
* Example: "(uid={0})"
*
* @param userSearchFilter the filter (must not be null)
* @return this configurer for method chaining
*/
public LdapAuthenticationProviderConfigurer<B> userSearchFilter(String userSearchFilter);
/**
* Sets base DN for group search.
*
* @param groupSearchBase the base DN
* @return this configurer for method chaining
*/
public LdapAuthenticationProviderConfigurer<B> groupSearchBase(String groupSearchBase);
/**
* Sets LDAP filter for group search.
*
* @param groupSearchFilter the filter
* @return this configurer for method chaining
*/
public LdapAuthenticationProviderConfigurer<B> groupSearchFilter(String groupSearchFilter);
/**
* Sets attribute name for group role.
*
* @param groupRoleAttribute the attribute name
* @return this configurer for method chaining
*/
public LdapAuthenticationProviderConfigurer<B> groupRoleAttribute(String groupRoleAttribute);
/**
* Sets role prefix (default: "ROLE_").
*
* @param rolePrefix the prefix
* @return this configurer for method chaining
*/
public LdapAuthenticationProviderConfigurer<B> rolePrefix(String rolePrefix);
/**
* Sets LDAP context source.
*
* @param contextSource the context source (must not be null)
* @return this configurer for method chaining
*/
public LdapAuthenticationProviderConfigurer<B> contextSource(
BaseLdapPathContextSource contextSource
);
/**
* Configures password comparison (instead of bind).
*
* @return password comparison configurer
*/
public PasswordComparisonConfigurer<B> passwordCompare();
/**
* Sets password encoder for password comparison.
*
* @param passwordEncoder the encoder
* @return this configurer for method chaining
*/
public LdapAuthenticationProviderConfigurer<B> passwordEncoder(PasswordEncoder passwordEncoder);
/**
* Sets password attribute name.
*
* @param passwordAttribute the attribute name
* @return this configurer for method chaining
*/
public LdapAuthenticationProviderConfigurer<B> passwordAttribute(String passwordAttribute);
/**
* Configurer for password comparison.
*/
public final class PasswordComparisonConfigurer<B2 extends ProviderManagerBuilder<B2>> {
public PasswordComparisonConfigurer<B2> passwordEncoder(PasswordEncoder passwordEncoder);
public PasswordComparisonConfigurer<B2> passwordAttribute(String passwordAttribute);
public LdapAuthenticationProviderConfigurer<B2> and();
}
}Usage Examples:
// Using Spring Boot auto-configuration
// application.yml
spring:
ldap:
urls: ldap://ldap.example.com:389
base: dc=example,dc=com
@Configuration
@EnableWebSecurity
public class LdapSecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(authorize -> authorize
.anyRequest().authenticated()
)
.formLogin(Customizer.withDefaults());
return http.build();
}
}
// Manual LDAP configuration with bind
@Bean
public AuthenticationManager authenticationManager(
BaseLdapPathContextSource contextSource) throws Exception {
LdapBindAuthenticationManagerFactory factory =
new LdapBindAuthenticationManagerFactory(contextSource);
factory.setUserDnPatterns("uid={0},ou=people");
factory.setUserSearchBase("ou=people");
factory.setUserSearchFilter("(uid={0})");
return factory.createAuthenticationManager();
}
// LDAP with password comparison
@Bean
public AuthenticationManager ldapAuthenticationManager(
BaseLdapPathContextSource contextSource,
PasswordEncoder encoder) {
LdapPasswordComparisonAuthenticationManagerFactory factory =
new LdapPasswordComparisonAuthenticationManagerFactory(
contextSource,
encoder
);
factory.setUserDnPatterns("uid={0},ou=people");
factory.setPasswordAttribute("userPassword");
return factory.createAuthenticationManager();
}
// Embedded LDAP server (for testing)
@Bean
public EmbeddedLdapServerContextSourceFactoryBean contextSourceFactoryBean() {
EmbeddedLdapServerContextSourceFactoryBean factory =
EmbeddedLdapServerContextSourceFactoryBean.fromEmbeddedLdapServer();
factory.setPort(8389);
return factory;
}Important Notes:
Thread Safety:
Performance:
Add custom authentication provider. Must be thread-safe for concurrent use.
/**
* Interface for custom authentication providers.
* Must be thread-safe for concurrent use.
*
* @since 3.0
*/
public interface AuthenticationProvider {
/**
* Authenticates the given authentication object.
*
* @param authentication the authentication to authenticate (must not be null)
* @return fully authenticated Authentication object (never null)
* @throws AuthenticationException if authentication fails
*/
Authentication authenticate(Authentication authentication) throws AuthenticationException;
/**
* Returns whether this provider supports the given authentication type.
*
* @param authentication the authentication class
* @return true if this provider supports the authentication type
*/
boolean supports(Class<?> authentication);
}Usage Example:
@Component
public class CustomAuthenticationProvider implements AuthenticationProvider {
@Autowired
private UserService userService;
@Override
public Authentication authenticate(Authentication authentication)
throws AuthenticationException {
String username = authentication.getName();
String password = authentication.getCredentials().toString();
// Custom authentication logic - must be thread-safe
User user = userService.findByUsername(username);
if (user == null || !userService.checkPassword(password, user.getPassword())) {
throw new BadCredentialsException("Invalid credentials");
}
// Check account status
if (!user.isEnabled()) {
throw new DisabledException("Account is disabled");
}
if (!user.isAccountNonLocked()) {
throw new LockedException("Account is locked");
}
// Create authenticated token
List<GrantedAuthority> authorities = userService.getAuthorities(user);
return new UsernamePasswordAuthenticationToken(
username, password, authorities
);
}
@Override
public boolean supports(Class<?> authentication) {
return UsernamePasswordAuthenticationToken.class
.isAssignableFrom(authentication);
}
}
// Configuration
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Autowired
private CustomAuthenticationProvider authenticationProvider;
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authenticationProvider(authenticationProvider) // Register provider
.authorizeHttpRequests(authorize -> authorize
.anyRequest().authenticated()
)
.formLogin(Customizer.withDefaults());
return http.build();
}
}Important Notes:
AuthenticationProvider must be thread-safesupports() determines which authentication types are handledAuthenticationException subclassesAuthentication objectThread Safety:
authenticate() may be called concurrentlyPerformance:
Configure password encoders for secure password storage. Password encoders are thread-safe.
/**
* Interface for encoding and validating passwords.
* Implementations must be thread-safe.
*
* @since 3.1
*/
public interface PasswordEncoder {
/**
* Encodes the raw password.
*
* @param rawPassword the raw password (must not be null)
* @return encoded password (never null)
*/
String encode(CharSequence rawPassword);
/**
* Validates if raw password matches encoded password.
*
* @param rawPassword the raw password (must not be null)
* @param encodedPassword the encoded password (must not be null)
* @return true if passwords match
*/
boolean matches(CharSequence rawPassword, String encodedPassword);
/**
* Returns whether encoded password should be upgraded.
*
* @param encodedPassword the encoded password
* @return true if password should be upgraded
*/
default boolean upgradeEncoding(String encodedPassword) {
return false;
}
}Usage Examples:
// BCrypt (recommended for most applications)
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
// Default strength: 10
// Higher strength = more secure but slower
}
// BCrypt with custom strength
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder(12); // Strength 4-31
}
// Delegating password encoder (supports multiple formats)
@Bean
public PasswordEncoder passwordEncoder() {
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
// Supports: {bcrypt}, {noop}, {pbkdf2}, {scrypt}, {sha256}, etc.
// Automatically detects format from password prefix
}
// Using password encoder
@Service
public class UserService {
@Autowired
private PasswordEncoder passwordEncoder;
public void createUser(String username, String rawPassword) {
String encodedPassword = passwordEncoder.encode(rawPassword);
// Save user with encoded password
userRepository.save(new User(username, encodedPassword));
}
public boolean checkPassword(String rawPassword, String encodedPassword) {
return passwordEncoder.matches(rawPassword, encodedPassword);
}
}
// Password formats with delegating encoder
// {bcrypt}$2a$10$dXJ3SW6G7P50lGmMkkmwe.20cQQubK3.HZWzG3YB1tlRy.fqvM/BG
// {noop}password // Plain text (testing only, never use in production)
// {pbkdf2}5d923b44a6d129f3ddf3e3c8d29412723dcbde72445e8ef6bf3b508fbf17fa4ed4d6b99ca763d8dc
// {scrypt}$e0801$8bWJaSu2IKSn9Z9kM+TPXfOc/9bdYSrN1oD9qfVThWEwdRTnO7re7Ei+fUZRJ68k9lTyuTeUp4of4g24hHnazw==$OAOec05+bXxvuu/1qZ6NUR+xQYvYv7BeL1QxwRpY5Pc=Important Notes:
{noop} prefix means no encoding - testing onlyThread Safety:
PasswordEncoder implementations are thread-safeencode() and matches() are thread-safe for concurrent usePerformance:
/**
* User information interface.
* Immutable implementations are thread-safe.
*
* @since 2.0
*/
public interface UserDetails extends Serializable {
/**
* Returns user authorities.
*
* @return authorities (never null)
*/
Collection<? extends GrantedAuthority> getAuthorities();
/**
* Returns user password (may be encoded).
*
* @return password (may be null)
*/
String getPassword();
/**
* Returns username.
*
* @return username (never null)
*/
String getUsername();
/**
* Returns whether account is non-expired.
*
* @return true if account is non-expired
*/
boolean isAccountNonExpired();
/**
* Returns whether account is non-locked.
*
* @return true if account is non-locked
*/
boolean isAccountNonLocked();
/**
* Returns whether credentials are non-expired.
*
* @return true if credentials are non-expired
*/
boolean isCredentialsNonExpired();
/**
* Returns whether account is enabled.
*
* @return true if account is enabled
*/
boolean isEnabled();
}
/**
* Interface for loading user-specific data.
* Must be thread-safe for concurrent access.
*
* @since 2.0
*/
public interface UserDetailsService {
/**
* Loads user by username.
*
* @param username the username (must not be null)
* @return user details (never null)
* @throws UsernameNotFoundException if user not found
*/
UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;
}
/**
* Interface for managing user details.
* Extends UserDetailsService with user management operations.
* Must be thread-safe for concurrent access.
*
* @since 2.0
*/
public interface UserDetailsManager extends UserDetailsService {
void createUser(UserDetails user);
void updateUser(UserDetails user);
void deleteUser(String username);
void changePassword(String oldPassword, String newPassword);
boolean userExists(String username);
}
/**
* Interface for encoding and validating passwords.
* Must be thread-safe.
*
* @since 3.1
*/
public interface PasswordEncoder {
String encode(CharSequence rawPassword);
boolean matches(CharSequence rawPassword, String encodedPassword);
default boolean upgradeEncoding(String encodedPassword) {
return false;
}
}
/**
* Authentication object interface.
* Represents authentication request or result.
*
* @since 2.0
*/
public interface Authentication extends Principal, Serializable {
Collection<? extends GrantedAuthority> getAuthorities();
Object getCredentials();
Object getDetails();
Object getPrincipal();
boolean isAuthenticated();
void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException;
}
/**
* Granted authority interface.
* Represents an authority granted to an authentication.
*
* @since 2.0
*/
public interface GrantedAuthority extends Serializable {
String getAuthority();
}@Configuration
@EnableWebSecurity
public class CompleteAuthenticationConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(authorize -> authorize
.anyRequest().authenticated()
)
.formLogin(Customizer.withDefaults())
.httpBasic(Customizer.withDefaults());
return http.build();
}
@Bean
public UserDetailsService userDetailsService(PasswordEncoder passwordEncoder) {
// In production, this would be a database-backed implementation
UserDetails user = User.withUsername("user")
.password(passwordEncoder.encode("password"))
.roles("USER")
.build();
UserDetails admin = User.withUsername("admin")
.password(passwordEncoder.encode("admin"))
.roles("USER", "ADMIN")
.build();
return new InMemoryUserDetailsManager(user, admin);
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
// Optional: Custom authentication manager
@Bean
public AuthenticationManager authenticationManager(
UserDetailsService userDetailsService,
PasswordEncoder passwordEncoder) {
DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
authProvider.setUserDetailsService(userDetailsService);
authProvider.setPasswordEncoder(passwordEncoder);
return new ProviderManager(authProvider);
}
}Symptoms: Login fails even with correct credentials.
Causes:
Solutions:
// Ensure password encoder matches
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
// Ensure UserDetailsService uses same encoder
@Bean
public UserDetailsService userDetailsService(PasswordEncoder encoder) {
return new InMemoryUserDetailsManager(
User.withUsername("user")
.password(encoder.encode("password")) // Encode password
.roles("USER")
.build()
);
}
// Or use password prefix
User.withUsername("user")
.password("{bcrypt}$2a$10$...") // Pre-encoded with prefix
.roles("USER")
.build();
// Check account status
User.withUsername("user")
.password(encoder.encode("password"))
.roles("USER")
.accountExpired(false) // Ensure account is not expired
.accountLocked(false) // Ensure account is not locked
.credentialsExpired(false) // Ensure credentials not expired
.disabled(false) // Ensure account is enabled
.build();Symptoms: UsernameNotFoundException or authentication fails with "User not found".
Causes:
Solutions:
// Ensure UserDetailsService bean exists
@Bean
public UserDetailsService userDetailsService() {
return new InMemoryUserDetailsManager(/* users */);
}
// Ensure UserDetailsService is registered
@Bean
public SecurityFilterChain filterChain(
HttpSecurity http,
UserDetailsService userDetailsService) throws Exception {
http
.userDetailsService(userDetailsService) // Register service
.authorizeHttpRequests(authorize -> authorize
.anyRequest().authenticated()
);
return http.build();
}Symptoms: Password encoding throws exception or doesn't work.
Causes:
Solutions:
// Ensure password encoder bean exists
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
// Use delegating encoder for multiple formats
@Bean
public PasswordEncoder passwordEncoder() {
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}
// Handle encoding errors
try {
String encoded = passwordEncoder.encode(rawPassword);
} catch (Exception e) {
// Handle encoding failure
logger.error("Password encoding failed", e);
}Symptoms: Database authentication fails with SQL errors.
Causes:
Solutions:
// Ensure DataSource is configured
@Bean
public DataSource dataSource() {
// Configure DataSource
}
// Use default schema
@Bean
public UserDetailsManager userDetailsManager(DataSource dataSource) {
JdbcUserDetailsManager manager = new JdbcUserDetailsManager(dataSource);
manager.setCreateUserSql("INSERT INTO users (username, password, enabled) VALUES (?, ?, ?)");
manager.setCreateAuthoritySql("INSERT INTO authorities (username, authority) VALUES (?, ?)");
return manager;
}
// Verify query column order
// usersByUsernameQuery must return: username, password, enabled
// authoritiesByUsernameQuery must return: username, authority
manager.setUsersByUsernameQuery(
"SELECT username, password, enabled FROM users WHERE username = ?"
);
manager.setAuthoritiesByUsernameQuery(
"SELECT username, authority FROM authorities WHERE username = ?"
);CachingUserDetailsService)@Bean methods (single-threaded during startup)Best Practices:
@Bean methods