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
-
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.