Implementing Password Hashing in Spring Boot – Java Guide
Fast recall
Quick revision
Core answer
PasswordEncoder bean, call encode whenever a password is set, store the complete encoded verifier, and call matches during authentication. For a new application, configure Argon2id with parameters your environment can support; for existing bcrypt data, keep a delegating or migration path and rehash after a successful login when upgradeEncoding says the value is old. Benchmark the work factor instead of copying a cost number.Interview-ready guide
Interview answer
In Spring Boot I expose a PasswordEncoder bean and inject the interface into registration and authentication services. When a password is created or changed, the service calls encode and stores the complete encoded verifier. During login it calls matches with the candidate and stored value. It must not encode the candidate again and compare strings because a new random salt produces a different result.
For a new application I would start with Argon2id and benchmark its memory and time settings under realistic login concurrency. For an existing system with bcrypt hashes, I would keep support for bcrypt while making the current encoder create Argon2id values. A delegating encoded format identifies which algorithm verifies each row.
After a successful match, upgradeEncoding can tell the service that the stored value no longer meets current policy. The service can then re-encode the candidate and save the new verifier. That gives a gradual migration without forcing every active user to reset at once. Rate limiting and capacity testing are also important because password verification is intentionally expensive.
Implementing Password Hashing in Spring Boot – Java Guide
Direct Answer
Expose one PasswordEncoder bean, call encode whenever a password is set, store the complete encoded verifier, and call matches during authentication. For a new application, configure Argon2id with parameters your environment can support; for existing bcrypt data, keep a delegating or migration path and rehash after a successful login when upgradeEncoding says the value is old. Benchmark the work factor instead of copying a cost number.
Code Example
@Configuration
class PasswordConfig {
@Bean
PasswordEncoder passwordEncoder() {
String id = "argon2";
Map<String, PasswordEncoder> encoders = Map.of(
"argon2", new Argon2PasswordEncoder(16, 32, 1, 19 * 1024, 2),
"bcrypt", new BCryptPasswordEncoder());
return new DelegatingPasswordEncoder(id, encoders);
}
}
@Service
class LoginService {
boolean verifyAndUpgrade(User user, CharSequence candidate) {
if (!encoder.matches(candidate, user.passwordHash())) return false;
if (encoder.upgradeEncoding(user.passwordHash())) {
user.changePasswordHash(encoder.encode(candidate));
repository.save(user);
}
return true;
}
}
Benchmark the chosen parameters and keep old algorithm mappings until their stored verifiers have been migrated or reset.
Common Mistakes & Pitfalls
- Creating encoder instances in service classes: Doing var encoder = new BCryptPasswordEncoder() inside a service method bypasses dependency injection and makes changing the cost factor a nightmare—you'd have to find every instance. Always inject the bean.
- Comparing hashes with equals: Never do storedHash.equals(encoder.encode(input))—this creates a new hash with a different salt, so it'll never match even for correct passwords. Always use encoder.matches(input, storedHash).
- Not specifying cost factor: Using the no-arg constructor new BCryptPasswordEncoder() defaults to cost 10, which might be too low for production. Explicitly set cost to 12 or higher in the bean declaration.
- Double-hashing passwords: Some freshers do encoder.encode(encoder.encode(password)) thinking it's more secure, but bcrypt output is alphanumeric strings—hashing a hash produces nonsense and breaks verification. Hash the raw password exactly once.
- Returning raw passwords in REST responses: After registration, never return the original password in JSON—return a success message and force the client to discard the password immediately.
Check your understanding
Answer these without looking back at the article.
- What happens if you change the cost factor after users have registered?
- How does DaoAuthenticationProvider use PasswordEncoder internally?
- What would you do if you needed to migrate existing MD5-hashed passwords to bcrypt?
Looking for a structured roadmap?
Every module on this page is also part of the full Java backend interview track.