Home  >  Spring 5

Spring Security 5 Default Password Encoder : DelegatingPasswordEncoder

By Arvind Rai, January 03, 2020
Spring Security DelegatingPasswordEncoder delegates to another PasswordEncoder based upon a prefixed identifier. The DelegatingPasswordEncoder is introduced in Spring Security 5.0 and is the default password encoder. Now the password format will be used as {id}EncodedPassword. Suppose password is {bcrypt}EncodedPassword then DelegatingPasswordEncoder will delegate it to BCryptPasswordEncoder. Before Spring Security 5.0, the default PasswordEncoder was NoOpPasswordEncoder which requires plain text password. Now in Spring Security 5.0, if we want to use plain text password, then the password format will be {noop}password and DelegatingPasswordEncoder will delegate it to NoOpPasswordEncoder automatically.
Here we will discuss using Spring Security 5 default password encoder in detail with examples.


Technologies Used

Find the technologies being used in our example.
1. Java 11
2. Spring 5.2.1.RELEASE
3. Spring Boot 2.2.1.RELEASE
4. Maven 3.5.2

Using DelegatingPasswordEncoder

The DelegatingPasswordEncoder is the default password encoder in Spring Security 5.0. To store a password using DelegatingPasswordEncoder, we need to use following format.
{id}EncodedPassword 
Where id is password encoder name. By default we use following id for password encoder.
1. noop for NoOpPasswordEncoder
2. bcrypt for BCryptPasswordEncoder
3. pbkdf2 for Pbkdf2PasswordEncoder
4. scrypt for SCryptPasswordEncoder
5. sha256 for StandardPasswordEncoder

We store password in file, database, LDAP, in-memory etc. Using prefixed id with password, the DelegatingPasswordEncoder delegates the password to respective encoder to handle it. Find the sample password storage format examples.
1.
{noop}ram123 
The DelegatingPasswordEncoder will delegate this password to NoOpPasswordEncoder.
2.
{bcrypt}$2a$10$q5pHs1fyVDbQSnBu3Il/meAONlMYFT1RhGlT2OC6IXX5.bp2JBZU6 
The DelegatingPasswordEncoder will delegate this password to BCryptPasswordEncoder .
3.
{pbkdf2}77a366e9ba2d6830b64d1b86eb7462996f3102d2b76562de3e56e5f13543a02486e1ee553cef18de 
The DelegatingPasswordEncoder will delegate this password to Pbkdf2PasswordEncoder.
4.
{scrypt}$e0801$VoXA+5ot9fXbuLaOj5izqgd3JrOtBjvsqpbwssgHIA3omPpNB8t+FYtnsVpxx5DJjAvFtrA/klDstepB320TSw==$CLsjnAxzzNof2jkDrSBeoCTvuxO8qi67FRDY0vrCZwY= 
The DelegatingPasswordEncoder will delegate this password to SCryptPasswordEncoder.
5.
{sha256}25ff1da3db4fc9bfab27acfc47625ac607a076450762fed1d53003ebeb73239e70529567767eee51 
The DelegatingPasswordEncoder will delegate this password to StandardPasswordEncoder.

Find the example for DelegatingPasswordEncoder password format. Here we are using in-memory authentication.
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
	auth.inMemoryAuthentication().withUser("ram")
	.password("{noop}ram123") //ram123
	.roles("USER")
	.and()
	.withUser("lakhan")
	.password("{bcrypt}$2a$10$q5pHs1fyVDbQSnBu3Il/meAONlMYFT1RhGlT2OC6IXX5.bp2JBZU6") //lakhan123
	.roles("USER")
	.and() 
	.withUser("bharat")
	.password("{pbkdf2}77a366e9ba2d6830b64d1b86eb7462996f3102d2b76562de3e56e5f13543a02486e1ee553cef18de") //bharat123
	.roles("USER")
	.and()
	.withUser("shatru")
	.password("{scrypt}$e0801$VoXA+5ot9fXbuLaOj5izqgd3JrOtBjvsqpbwssgHIA3omPpNB8t+FYtnsVpxx5DJjAvFtrA/klDstepB320TSw==$CLsjnAxzzNof2jkDrSBeoCTvuxO8qi67FRDY0vrCZwY=") //shatru123
	.roles("USER")
	.and()
	.withUser("bali")
	.password("{sha256}25ff1da3db4fc9bfab27acfc47625ac607a076450762fed1d53003ebeb73239e70529567767eee51") //bali123
	.roles("USER");		
} 
If we do not want to use password encoder id and by default DelegatingPasswordEncoder should delegate to our any specific password encoder such as BCryptPasswordEncoder, we just have to create its bean in our Java configuration.
@Bean
public static BCryptPasswordEncoder passwordEncoder() {
    return new BCryptPasswordEncoder();
} 
Now the password format must be without id.
$2a$10$q5pHs1fyVDbQSnBu3Il/meAONlMYFT1RhGlT2OC6IXX5.bp2JBZU6 
The DelegatingPasswordEncoder will delegate above password to BCryptPasswordEncoder automatically.
Find the XML configuration to create BCryptPasswordEncoder bean.
<bean name="passwordEncoder" 
          class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder"/> 


We can instantiate DelegatingPasswordEncoder as following in our application.
PasswordEncoder passwordEncoder = PasswordEncoderFactories.createDelegatingPasswordEncoder(); 

Custom DelegatingPasswordEncoder

We can prepare a custom DelegatingPasswordEncoder instance to be configured with Spring Security application. Prepare a Map with password encoder id and PasswordEncoder instance.
Map<String, PasswordEncoder> encoders = new HashMap<>();
encoders.put("myBcrypt", new BCryptPasswordEncoder());
encoders.put("myPbkdf2", new Pbkdf2PasswordEncoder());
encoders.put("myScrypt", new SCryptPasswordEncoder()); 
Look into the DelegatingPasswordEncoder constructor.
public DelegatingPasswordEncoder(String idForEncode, Map<String,
                       PasswordEncoder> idToPasswordEncoder) 
idForEncode is the id of PasswordEncoder used to encode raw password using encode(CharSequence rawPassword) method.
idToPasswordEncoder is the Map of password encoder id and PasswordEncoder instance.
So we create instance of DelegatingPasswordEncoder as following.
PasswordEncoder passwordEncoder =
    new DelegatingPasswordEncoder("myBcrypt", encoders); 
Find the sample example to configure custom DelegatingPasswordEncoder instance with in-memory authentication.
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
	Map<String, PasswordEncoder> encoders = new HashMap<>();
	encoders.put("myBcrypt", new BCryptPasswordEncoder());
	encoders.put("myPbkdf2", new Pbkdf2PasswordEncoder());
	encoders.put("myScrypt", new SCryptPasswordEncoder());

	PasswordEncoder passwordEncoder =
		new DelegatingPasswordEncoder("myBcrypt", encoders);
	
	auth.inMemoryAuthentication().passwordEncoder(passwordEncoder)
	.withUser("lakhan")
	.password("{myBcrypt}$2a$10$q5pHs1fyVDbQSnBu3Il/meAONlMYFT1RhGlT2OC6IXX5.bp2JBZU6") //lakhan123
	.roles("USER")
	.and() 
	.withUser("bharat")
	.password("{myPbkdf2}77a366e9ba2d6830b64d1b86eb7462996f3102d2b76562de3e56e5f13543a02486e1ee553cef18de") //bharat123
	.roles("USER")
	.and()
	.withUser("shatru")
	.password("{myScrypt}$e0801$VoXA+5ot9fXbuLaOj5izqgd3JrOtBjvsqpbwssgHIA3omPpNB8t+FYtnsVpxx5DJjAvFtrA/klDstepB320TSw==$CLsjnAxzzNof2jkDrSBeoCTvuxO8qi67FRDY0vrCZwY=") //shatru123
	.roles("USER");
} 

Troubleshooting

Suppose password encoder id is missing in password format.
$2a$10$q5pHs1fyVDbQSnBu3Il/meAONlMYFT1RhGlT2OC6IXX5.bp2JBZU6 
We will get error as following in Spring Security 5.0.
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) 
The error is because the password storage format is not correct. The id is missing in password format. We can fix it by prefixing password encoder id.
{myBcrypt}$2a$10$q5pHs1fyVDbQSnBu3Il/meAONlMYFT1RhGlT2OC6IXX5.bp2JBZU6 

Complete Example

We will create a Spring Security 5 login application to demo the default password encoder.
pom.xml
<parent>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-parent</artifactId>
	<version>2.2.1.RELEASE</version>
	<relativePath />
</parent>
<properties>
	<context.path>spring-app</context.path>
	<java.version>11</java.version>
</properties>
<dependencies>
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-web</artifactId>
	</dependency>
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-security</artifactId>
	</dependency>
</dependencies> 
SecurityConfig.java
package com.concretepage;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
	@Override
	protected void configure(HttpSecurity http) throws Exception {
		http.authorizeRequests()
		   .antMatchers("/**").access("hasRole('USER')")
		   .and().formLogin();
	}

	@Override
	protected void configure(AuthenticationManagerBuilder auth) throws Exception {
		auth.inMemoryAuthentication().withUser("ram")
		.password("{noop}ram123") //ram123
		.roles("USER")
		.and()
		.withUser("lakhan")
		.password("{bcrypt}$2a$10$q5pHs1fyVDbQSnBu3Il/meAONlMYFT1RhGlT2OC6IXX5.bp2JBZU6") //lakhan123
		.roles("USER")
		.and() 
		.withUser("bharat")
		.password("{pbkdf2}77a366e9ba2d6830b64d1b86eb7462996f3102d2b76562de3e56e5f13543a02486e1ee553cef18de") //bharat123
		.roles("USER")
		.and()
		.withUser("shatru")
		.password("{scrypt}$e0801$VoXA+5ot9fXbuLaOj5izqgd3JrOtBjvsqpbwssgHIA3omPpNB8t+FYtnsVpxx5DJjAvFtrA/klDstepB320TSw==$CLsjnAxzzNof2jkDrSBeoCTvuxO8qi67FRDY0vrCZwY=") //shatru123
		.roles("USER")
		.and()
		.withUser("bali")
		.password("{sha256}25ff1da3db4fc9bfab27acfc47625ac607a076450762fed1d53003ebeb73239e70529567767eee51") //bali123
		.roles("USER");		
	}
} 
AppController.java
package com.concretepage;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
public class AppController {
	@GetMapping("/app")
	public @ResponseBody String helloUser(Authentication authentication) {
		return "Welcome! " + authentication.getName();
	}
} 
Main.java
package com.concretepage;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Main {
	public static void main(String[] args) {
		SpringApplication.run(Main.class, args);
	}
} 

Output

Download the project and run the following command from root folder of the project using command prompt.
mvn spring-boot:run 
Access the URL.
http://localhost:8080/app 
Enter credentials lakhan/lakhan123
Spring Security 5 Default Password Encoder
We will see welcome page as following.
Spring Security 5 Default Password Encoder

References

Spring Security Reference
Spring doc: DelegatingPasswordEncoder

Download Source Code

POSTED BY
ARVIND RAI
ARVIND RAI








©2020 concretepage.com | Privacy Policy | Contact Us