Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How override the default BCryptPasswordEncoder created through PasswordEncoderFactories?

I know that in Spring Security would arise the following:

There was an unexpected error (type=Internal Server Error, status=500).
There is no PasswordEncoder mapped for the id "null"
java.lang.IllegalArgumentException: There is no PasswordEncoder mapped for the id "null"

And the solution is define a PasswordEncoder. For simplicity is possible define the following:

@Bean
PasswordEncoder encoder() {
    return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}

Now, behind the scene the createDelegatingPasswordEncoder() method is defined how (it so far until currently for Spring Security 5.4.2) (See the PasswordEncoderFactories class for more details):

@SuppressWarnings("deprecation")
public static PasswordEncoder createDelegatingPasswordEncoder() {
    String encodingId = "bcrypt";
    Map<String, PasswordEncoder> encoders = new HashMap<>();
    encoders.put(encodingId, new BCryptPasswordEncoder());
    encoders.put("ldap", new org.springframework.security.crypto.password.LdapShaPasswordEncoder());
    encoders.put("MD4", new org.springframework.security.crypto.password.Md4PasswordEncoder());
    encoders.put("MD5", new org.springframework.security.crypto.password.MessageDigestPasswordEncoder("MD5"));
    encoders.put("noop", org.springframework.security.crypto.password.NoOpPasswordEncoder.getInstance());
    encoders.put("pbkdf2", new Pbkdf2PasswordEncoder());
    encoders.put("scrypt", new SCryptPasswordEncoder());
    encoders.put("SHA-1", new org.springframework.security.crypto.password.MessageDigestPasswordEncoder("SHA-1"));
    encoders.put("SHA-256",
            new org.springframework.security.crypto.password.MessageDigestPasswordEncoder("SHA-256"));
    encoders.put("sha256", new org.springframework.security.crypto.password.StandardPasswordEncoder());
    encoders.put("argon2", new Argon2PasswordEncoder());
    return new DelegatingPasswordEncoder(encodingId, encoders);
}

Now about the BCryptPasswordEncoder class, it works with some defaults such as:

  • version: $2a
  • strength: 10

What happens if declare the following:

@Bean
PasswordEncoder bcryptEncoder() {
    return new BCryptPasswordEncoder(BCryptVersion.$2Y, 12);
}

How I can add or override the default BCryptPasswordEncoder created with the custom BCryptPasswordEncoder into the default settings? I want keep all the other defaults

Note: In the PasswordEncoderFactories class (for the createDelegatingPasswordEncoder method), the DelegatingPasswordEncoder class is used behind the scenes. That class does not offer an approach to override too.

like image 797
Manuel Jordan Avatar asked Dec 02 '25 06:12

Manuel Jordan


1 Answers

If you are creating a new application, you very likely do not need DelegatingPasswordEncoder and instead should use an adaptive one-way function password encoder, for example BCryptPasswordEncoder.

To do this, you can expose a BCryptPasswordEncoder as a bean.

@Bean
PasswordEncoder bcryptEncoder() {
    return new BCryptPasswordEncoder(BCryptVersion.$2Y, 12);
}

Then, when a user registers, you can encode their password using the BCryptPasswordEncoder before saving it to your data store. For example:

UserDetails userDetails = User
        .withUsername(username)
        .password(bcryptEncoder.encode(password))
        .roles("USER")
        .build();

If you are migrating an existing application, that is where DelegatingPasswordEncoder is useful.

The DelegatingPasswordEncoder allows for validating passwords in multiple formats. It uses the prefixed ID (e.g {bcrypt}) to look up which PasswordEncoder should be used.

Consider a legacy application that uses plaintext passwords.
To migrate the application, you would prefix the plaintext passwords with {noop} and use a DelegatingPasswordEncoder like the one below:

@Bean
public PasswordEncoder delegatingPasswordEncoder() {
    Map<String, PasswordEncoder> encoders = new HashMap<>();
    encoders.put("bcrypt", new BCryptPasswordEncoder(BCryptVersion.$2Y, 12));
    encoders.put("noop", NoOpPasswordEncoder.getInstance());
    return new DelegatingPasswordEncoder("bcrypt", encoders);
}

This DelegatingPasswordEncoder will encode and validate any newly created password using the BCryptPasswordEncoder, while still being able to validate the legacy plaintext passwords using the NoOpPasswordEncoder.

Spring Security provides the PasswordEncoderFactories.createDelegatingPasswordEncoder() method as a convenient default, but it is unlikely that your application uses that many different password encodings.
What is more likely, is that your application uses 2 different encodings, the legacy one (e.g noop) and the modern one (e.g bcrypt), in which case you can use a PasswordEncoder similar to the delegatingPasswordEncoder described above.

The point of these examples is to say that in most situations you do not need the defaults that are set in createDelegatingPasswordEncoder. However, if you still want to use the encoders from createDelegatingPasswordEncoder, except for bcrypt, you can use a PasswordEncoder that looks like this:

@Bean
public PasswordEncoder delegatingPasswordEncoder() {
    Map<String, PasswordEncoder> encoders = new HashMap<>();
    // Use this encoder for bcrypt
    encoders.put("bcrypt", new BCryptPasswordEncoder(BCryptVersion.$2Y, 12));
    DelegatingPasswordEncoder delegatingPasswordEncoder =
            new DelegatingPasswordEncoder("bcrypt", encoders);

    PasswordEncoder defaultDelegatingPasswordEncoder =
            PasswordEncoderFactories.createDelegatingPasswordEncoder();
    // If a password ID does not match "bcrypt", use defaultDelegatingPasswordEncoder
    delegatingPasswordEncoder.setDefaultPasswordEncoderForMatches(defaultDelegatingPasswordEncoder);

    return delegatingPasswordEncoder;
}
like image 133
Eleftheria Stein-Kousathana Avatar answered Dec 04 '25 10:12

Eleftheria Stein-Kousathana