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

This is an in-memory users setup for a Spring Security demo that I'm using since Spring 4:

SecurityConfig.java
package spring.websocket.chat.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

/**
 * Security Config.
 * Defines spring security beans and configurations.
 */
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder authBuilder) throws Exception {
        authBuilder.inMemoryAuthentication().withUser("xavier").password("ProfessorX").roles("ADMIN");
        authBuilder.inMemoryAuthentication().withUser("logan").password("Wolverine").roles("USER");
        authBuilder.inMemoryAuthentication().withUser("scott").password("Cyclops").roles("USER");
        authBuilder.inMemoryAuthentication().withUser("ororo").password("Storm").roles("USER");
    }
}
                    

Lately, I upgrading this project to Spring 5, and during login, I encountered this issue:

java.lang.IllegalArgumentException: There is no PasswordEncoder mapped for the id "null" org.springframework.security.crypto.password.DelegatingPasswordEncoder$UnmappedIdPasswordEncoder.matches(DelegatingPasswordEncoder.java:250) org.springframework.security.crypto.password.DelegatingPasswordEncoder.matches(DelegatingPasswordEncoder.java:198) org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration$LazyPasswordEncoder.matches(AuthenticationConfiguration.java:312) org.springframework.security.authentication.dao.DaoAuthenticationProvider.additionalAuthenticationChecks(DaoAuthenticationProvider.java:90) org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider.authenticate(AbstractUserDetailsAuthenticationProvider.java:166) org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:175) org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:195)

Searching the internet, the solution for this issue is quite simple, and well documented here. Prior to Spring Security 5.0 the default PasswordEncoder was NoOpPasswordEncoder which required plain text passwords.

Spring Security 5.0 defaulted to DelegatingPasswordEncoder which:

  • Ensuring that passwords are encoded using the current password storage recommendations
  • Allowing for validating passwords in modern and legacy formats
  • Allowing for upgrading the encoding in the future

"Password storage recommendations" refer to Password Storage Format, which generally following this format:

{id}encodedPassword

id is an identifier used to look up which PasswordEncoder should be used and encodedPassword is the original encoded password for the selected PasswordEncoder. Here list of id for different PasswordEncoder:

idPasswordEncoderDescription
noopNoOpPasswordEncoderplain text password
bcryptBCryptPasswordEncoderbcrypt is a password hashing function based on the Blowfish cipher
pbkdf2Pbkdf2PasswordEncoderPBKDF2 (Password-Based Key Derivation Function 2) are key derivation functions with a sliding computational cost, used to reduce vulnerabilities to brute force attacks
scryptSCryptPasswordEncoderscrypt (pronounced "ess crypt") is a password-based key derivation function (algorithm) that specifically designed to make large-scale custom hardware attacks costly by requiring large amounts of memory
sha256StandardPasswordEncoderSHA-256 — part of SHA-2 (Secure Hash Algorithm 2) family, is novel hash functions computed with 32-bit words.

In our case id should be noop which refer to NoOpPasswordEncoder, and the original encoded password is in plain text format. So we need to change it like this:

authBuilder.inMemoryAuthentication().withUser("xavier").password("{noop}ProfessorX").roles("ADMIN");
authBuilder.inMemoryAuthentication().withUser("logan").password("{noop}Wolverine").roles("USER");
authBuilder.inMemoryAuthentication().withUser("scott").password("{noop}Cyclops").roles("USER");
authBuilder.inMemoryAuthentication().withUser("ororo").password("{noop}Storm").roles("USER");
                    

NullPointerException InMemoryUserDetailsManager.updatePassword

After change, I encountered this error when running my web application, after login:

java.lang.NullPointerException org.springframework.security.provisioning.InMemoryUserDetailsManager.updatePassword(InMemoryUserDetailsManager.java:147) org.springframework.security.authentication.dao.DaoAuthenticationProvider.createSuccessAuthentication(DaoAuthenticationProvider.java:135) org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider.authenticate(AbstractUserDetailsAuthenticationProvider.java:197) org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:174) org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:199)

To "fix" this issue, what I need to do is only upgrade my Spring Security from 5.1.1.RELEASE to 5.2.1.RELEASE. Please refer to https://github.com/spring-projects/spring-security/issues/6103