skip to content

PasswordEncoder & DelegatingPasswordEncoder

DelegatingPasswordEncoder stores an algorithm id alongside the hash, so a database can hold bcrypt and argon2 side by side and upgrade on login. Interviewers ask about the work factor and about how you migrate a legacy hash format.

part ofSpring Frameworkoverview, primer and where to startread it →
on this pageshow

questions

5

What is Spring Security's PasswordEncoder and why should you use it instead of storing passwords directly?

level: juniorimportance: must knowfreq 80%

answer

  1. encode() + matches(), never decrypt
  2. one-way, salted, slow
  3. leak-proof: store hash not plaintext
  4. DaoAuthenticationProvider calls matches()
  5. DelegatingPasswordEncoder is the recommended bean

basics

~20 s

PasswordEncoder is an interface that turns a raw password into a one-way, salted hash. You store the hash, never the plaintext, so a database leak doesn't expose real passwords. Its matches() method verifies a login attempt.

solid answer

~40 s

PasswordEncoder is Spring Security's SPI (service provider interface) for one-way password hashing. It has two core methods: encode(rawPassword) produces a hash to store, and matches(rawPassword, storedHash) checks a login attempt without ever reversing the hash. You store only the hash, so a leaked database doesn't reveal real passwords. Good encoders (BCrypt, Argon2) are deliberately slow and add a per-password random salt, which defeats rainbow tables and slows brute force. The recommended bean is PasswordEncoderFactories.createDelegatingPasswordEncoder(), which prefixes each hash with an algorithm id like {bcrypt}. DaoAuthenticationProvider uses this encoder automatically when authenticating a UserDetails, calling matches() rather than comparing strings, which also guards against timing attacks.

code

java · 11 lines
java
@Bean
PasswordEncoder passwordEncoder() {
    // {bcrypt}$2a$10$... — default DelegatingPasswordEncoder
    return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}

// Registration
String hash = passwordEncoder.encode("s3cret"); // store this

// Login (Spring does this inside DaoAuthenticationProvider)
boolean ok = passwordEncoder.matches("s3cret", hash); // true

go deeper

for a junior

Know it's one-way hashing, you store the hash, and matches() verifies login.

for a middle

Explain salting, why slow hashing matters, and that DaoAuthenticationProvider calls matches().

for a senior

Discuss timing-safe comparison, non-deterministic encoding, and the DelegatingPasswordEncoder default bean.

for a principal

Frame password storage as a security-lifecycle concern: algorithm agility, cost tuning, and breach containment.

## What PasswordEncoder is `org.springframework.security.crypto.password.PasswordEncoder` is an interface (an SPI — service provider interface) with three methods: - `String encode(CharSequence rawPassword)` — hashes a plaintext password into a storable string. - `boolean matches(CharSequence rawPassword, String encodedPassword)` — checks whether a raw password corresponds to a stored hash. It re-hashes the raw input (using the salt embedded in the stored hash) and compares; it never decrypts anything. - `default boolean upgradeEncoding(String encodedPassword)` — returns whether the stored hash should be re-encoded with stronger parameters (default `false`). ## Why hashing, not plaintext or encryption - **Plaintext**: a database leak instantly exposes every real password, and users reuse passwords across sites. - **Encryption** is reversible — if an attacker gets the key, they get every password. Passwords should be **one-way hashed** so there is no key to steal. - A proper password hash is **slow by design** (unlike SHA-256, which is fast and therefore weak for passwords) and includes a **random salt** per password so identical passwords produce different hashes, defeating precomputed rainbow tables. ## How it fits into Spring Security `DaoAuthenticationProvider` loads a `UserDetails` (whose `getPassword()` is the stored hash) and calls `passwordEncoder.matches(presentedPassword, storedHash)`. If it returns `true`, authentication succeeds. You never compare strings yourself; `matches()` is written to run in roughly constant time to reduce timing side-channels. ## The recommended bean Since Spring Security 5, the idiomatic bean is: ```java @Bean PasswordEncoder passwordEncoder() { return PasswordEncoderFactories.createDelegatingPasswordEncoder(); } ``` This returns a `DelegatingPasswordEncoder` whose default algorithm is BCrypt and whose stored hashes carry an `{id}` prefix such as `{bcrypt}$2a$10$...`. ## Common gotchas - **`NoOpPasswordEncoder`** stores plaintext and exists only for legacy tests/demos — it is `@Deprecated` and must never reach production. - If you store a hash with **no `{id}` prefix** while using a `DelegatingPasswordEncoder`, `matches()` throws `IllegalArgumentException: There is no PasswordEncoder mapped for the id "null"`. - Encoding is **non-deterministic**: calling `encode("secret")` twice yields different strings (different salts), so you must verify with `matches()`, never by re-encoding and string-comparing.

  • Why is SHA-256 a poor choice for hashing passwords even with a salt?
    SHA-256 is designed to be extremely fast, so an attacker can try billions of guesses per second on stolen hashes. Password hashes must be deliberately slow and tunable (BCrypt, Argon2, PBKDF2) to make brute force expensive.
  • Why does encode() return a different value each time for the same password?
    Each call generates a fresh random salt embedded in the output, so identical passwords produce different hashes. That is why you verify with matches() (which reads the stored salt) rather than re-encoding and comparing strings.

saying these in an interview costs you the question

  • Thinking PasswordEncoder encrypts (reversible) rather than hashes (one-way)
  • Saying you verify a password by re-encoding it and comparing strings
  • Using NoOpPasswordEncoder or plain SHA/MD5 in production
  • Believing the same password always produces the same hash

context

open as a page

How does DelegatingPasswordEncoder work, and what is the significance of the {id} prefix in a stored hash?

level: middleimportance: must knowfreq 75%

basics

~20 s

DelegatingPasswordEncoder stores each hash with a prefix like {bcrypt} or {argon2} that names the algorithm. On matches(), it reads the prefix, picks the matching encoder, and delegates. This lets one bean verify hashes made by different algorithms.

open as a page

What is the BCryptPasswordEncoder work factor (strength), what does it control, and how do you choose it?

level: middleimportance: should knowfreq 65%

basics

~20 s

The work factor (strength) sets how many hashing rounds BCrypt does: 2^strength iterations. Higher strength means slower hashing and harder brute force. The default is 10; typical range is 10-12. It's stored inside the hash.

open as a page

What does PasswordEncoder.upgradeEncoding do, and how does Spring Security use it to migrate stored passwords transparently?

level: seniorimportance: should knowfreq 55%

basics

~20 s

upgradeEncoding(storedHash) returns true when a hash was made with an older algorithm or lower strength than the current default. After a successful login, DaoAuthenticationProvider checks it and, if true, re-hashes the password with the new settings via a UserDetailsPasswordService.

open as a page

You must migrate a production user base from {bcrypt} to {argon2} with zero downtime and no forced password resets. How do you design this with Spring Security's password APIs?

level: principalimportance: nice to knowfreq 35%

basics

~20 s

Use a DelegatingPasswordEncoder whose default encode id is argon2 but whose map still contains bcrypt for verification. Provide a UserDetailsPasswordService so that upgradeEncoding re-hashes each user to {argon2} on their next successful login. Old logins keep working throughout.

open as a page