Default Password Encoder in Spring Security 5

1. Overview

In Spring Security 4, it was possible to store passwords in plain text using in-memory authentication.

A major overhaul of the password management process in version 5 has introduced more secure default mechanism for encoding and decoding passwords. This means that if your Spring application stores passwords in plain text, upgrading to Spring Security 5 may cause problems.

In this short tutorial, we’ll describe one of those potential problems and demonstrate a solution to the issue.

2. Spring Security 4

We’ll start by showing a standard security configuration that provides simple in-memory authentication (valid for Spring 4):

@Configuration
public class InMemoryAuthWebSecurityConfigurer
  extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(AuthenticationManagerBuilder auth)
      throws Exception {
        auth.inMemoryAuthentication()
          .withUser("spring")
          .password("secret")
          .roles("USER");
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
          .antMatchers("/private/**")
          .authenticated()
          .antMatchers("/public/**")
          .permitAll()
          .and()
          .httpBasic();
    }
}

This configuration defines authentication for all /private/ mapped methods and public access for everything under /public/.

If we use the same configuration under Spring Security 5, we’d get the following error:

java.lang.IllegalArgumentException: There is no PasswordEncoder mapped for the id "null"

The error tells us that the given password couldn’t be decoded since no password encoder was configured for our in-memory authentication.

3. Spring Security 5

We can fix this the error by defining a DelegatingPasswordEncoder with the PasswordEncoderFactories class.

We use this encoder to configure our user with the AuthenticationManagerBuilder:

@Configuration
public class InMemoryAuthWebSecurityConfigurer
  extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(AuthenticationManagerBuilder auth)
      throws Exception {
        PasswordEncoder encoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();
        auth.inMemoryAuthentication()
          .withUser("spring")
          .password(encoder.encode("secret"))
          .roles("USER");
    }
}

Now, with this configuration we’re storing our in-memory password using BCrypt in the following format:

{bcrypt}$2a$10$MF7hYnWLeLT66gNccBgxaONZHbrSMjlUofkp50sSpBw2PJjUqU.zS

Although we can define our own set of password encoders, it’s recommended to stick with the default encoders provided in PasswordEncoderFactories.

3.1. Migrating Existing Passwords

We can update existing passwords to the recommended Spring Security 5 standards by:

  • Updating plain text stored passwords with their value encoded:

String encoded = new BCryptPasswordEncoder().encode(plainTextPassword);
  • Prefixing hashed stored passwords with their known encoder identifier:

{bcrypt}$2a$10$MF7hYnWLeLT66gNccBgxaONZHbrSMjlUofkp50sSpBw2PJjUqU.zS
{sha256}97cde38028ad898ebc02e690819fa220e88c62e0699403e94fff291cfffaf8410849f27605abcbc0
  • Requesting users to update their passwords when the encoding-mechanism for stored passwords is unknown

4. Conclusion

In this quick example, we updated a valid a Spring 4 in-memory authentication configuration to Spring 5 using the new password storage mechanism.

As always, you can find the source code over on the GitHub project.

Leave a Reply

Your email address will not be published.