Spring Security Password Encoding

By Arvind Rai, January 06, 2020
This page will walk through Spring Security password encoding example. In Spring Security, the PasswordEncoder interface is implemented to provide different type of password encoder such as Argon2PasswordEncoder, BCryptPasswordEncoder etc. In Spring Security 5.0, the default password encoder is DelegatingPasswordEncoder.

To configure password encoder in DaoAuthenticationProvider, it provides setPasswordEncoder method.
For LDAP authentication provider we can use passwordEncoder method of following inner class.
LdapAuthenticationProviderConfigurer.PasswordCompareConfigurer 
In XML configuration, to use password encoder, create a bean for password encoder and assign it using <password-encoder> element within <authentication-provider> element.
Here on this page we will discuss to use different type of password encoder 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

PasswordEncoder

Spring Security provides PasswordEncoder interface that has different implementations for password encoding. The PasswordEncoder has following methods.
1.
String encode(CharSequence rawPassword) 
Encodes raw password.
2.
boolean  matches(CharSequence rawPassword, String encodedPassword) 
Verifies the encoded password.
3.
default boolean	 upgradeEncoding(String encodedPassword) 
Checks if encoded password should be encoded again or not.

Find the implementations of PasswordEncoder interface.
1. Argon2PasswordEncoder, 2. BCryptPasswordEncoder
3. DelegatingPasswordEncoder, 4. LdapShaPasswordEncoder
5. Md4PasswordEncoder, 6. MessageDigestPasswordEncoder
7. NoOpPasswordEncoder, 8. Pbkdf2PasswordEncoder
9. SCryptPasswordEncoder, 10. StandardPasswordEncoder

Find the test class that shows encoding and matching password using different password encoders.
PasswordEncoderTest.java
package com.concretepage;
import static org.junit.jupiter.api.Assertions.assertTrue;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.security.crypto.argon2.Argon2PasswordEncoder;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.crypto.password.Pbkdf2PasswordEncoder;
import org.springframework.security.crypto.scrypt.SCryptPasswordEncoder;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;

@ExtendWith(SpringExtension.class)
@ContextConfiguration
public class PasswordEncoderTest {
	@Test
	public void testBCrypt() {
		BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
		String result = encoder.encode("ram123");
		assertTrue(encoder.matches("ram123", result));		
	}
	@Test
	public void testArgon2() {
		Argon2PasswordEncoder encoder = new Argon2PasswordEncoder();
		String result = encoder.encode("ram123");
		assertTrue(encoder.matches("ram123", result));		
	}
	@Test
	public void testPbkdf2() {
		Pbkdf2PasswordEncoder encoder = new Pbkdf2PasswordEncoder();
		String result = encoder.encode("ram123");
		assertTrue(encoder.matches("ram123", result));		
	}
	@Test
	public void testSCrypt() {
		SCryptPasswordEncoder encoder = new SCryptPasswordEncoder();
		String result = encoder.encode("ram123");
		assertTrue(encoder.matches("ram123", result));		
	}	
	@Test
	public void testDelegatingPasswordEncoder() {
		PasswordEncoder encoder = PasswordEncoderFactories.createDelegatingPasswordEncoder(); 
		String result = encoder.encode("ram123");
		System.out.println(result);
		assertTrue(encoder.matches("ram123", result));		
	}	
} 
Find the print screen of the test result.
Spring Security Password Encoding

BCryptPasswordEncoder

The BCryptPasswordEncoder is the implementation of PasswordEncoder interface that uses the BCrypt strong hashing function. To instantiate BCryptPasswordEncoder we can optionally pass BCrypt version and strength. Larger the strength value, more the work will be done to hash the password. We encode and match the password using BCryptPasswordEncoder as following.
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
String result = encoder.encode("ram123");
assertTrue(encoder.matches("ram123", result)); 
In our Spring Security Java configuration, to enable a specific password encoder, just create a bean for that password encoder.
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
	@Override
	protected void configure(AuthenticationManagerBuilder auth) throws Exception {
		auth.inMemoryAuthentication()
		.withUser("lakhan")
		.password("$2a$10$q5pHs1fyVDbQSnBu3Il/meAONlMYFT1RhGlT2OC6IXX5.bp2JBZU6")
		.roles("USER");
	}
	@Bean
	public PasswordEncoder passwordEncoder() {
		BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
		return passwordEncoder;
	}	
        ------
} 
Find the bean to create in XML configuration and assign it using <password-encoder> element.
<authentication-manager>
	<authentication-provider>
		<password-encoder ref="passwordEncoder"/>
		<user-service>
			<user name="lakhan"
				 password="$2a$10$q5pHs1fyVDbQSnBu3Il/meAONlMYFT1RhGlT2OC6IXX5.bp2JBZU6" 
				 authorities="ROLE_USER" />		        
		</user-service>	
	</authentication-provider>
</authentication-manager>

<beans:bean name="passwordEncoder" 
	  class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder"/> 

Argon2PasswordEncoder

The Argon2PasswordEncoder is the implementation of PasswordEncoder interface that uses the Argon2 hashing function. To instantiate Argon2PasswordEncoder we can optionally pass the length of the salt, length of the generated hash, CPU cost parameter, memory cost parameter and parallelization parameter. To use Argon2PasswordEncoder, we need to resolve Bouncy castle dependency.
<dependency>
	<groupId>org.bouncycastle</groupId>
	<artifactId>bcprov-jdk15on</artifactId>
	<version>1.64</version>
</dependency> 
We can encode and match password using Argon2PasswordEncoder as following.
Argon2PasswordEncoder encoder = new Argon2PasswordEncoder();
String result = encoder.encode("ram123");
assertTrue(encoder.matches("ram123", result)); 
In Spring Security Java configuration, to enable Argon2PasswordEncoder, just create its bean.
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
	@Override
	protected void configure(AuthenticationManagerBuilder auth) throws Exception {
		auth.inMemoryAuthentication()
		.withUser("ram")
		.password("$argon2id$v=19$m=4096,t=3,p=1$JudTbnHxqeGVs608XXVcTA$WB7CJvel59tcXBbsXhyq60et7wutd1DYSFufFX9uhtM")
		.roles("USER");
	}
	@Bean
	public PasswordEncoder passwordEncoder() {
		Argon2PasswordEncoder passwordEncoder = new Argon2PasswordEncoder();
		return passwordEncoder;
	}	
        ------
} 
Find the bean to create in XML configuration and assign it using <password-encoder> element.
<beans:bean name="passwordEncoder" 
          class="org.springframework.security.crypto.argon2.Argon2PasswordEncoder"/> 

Pbkdf2PasswordEncoder

The Pbkdf2PasswordEncoder is the implementation of PasswordEncoder interface that uses PBKDF2 hashing function. To instantiate Pbkdf2PasswordEncoder, we can optionally pass secret value to be included in the password hash, number of iteration and hash size. Find the example to encode and match password using Pbkdf2PasswordEncoder.
Pbkdf2PasswordEncoder encoder = new Pbkdf2PasswordEncoder();
String result = encoder.encode("ram123");
assertTrue(encoder.matches("ram123", result)); 
To use Pbkdf2PasswordEncoder in Spring Security Java configuration, just create its bean.
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
	@Override
	protected void configure(AuthenticationManagerBuilder auth) throws Exception {
		auth.inMemoryAuthentication()
		.withUser("bharat")
		.password("77a366e9ba2d6830b64d1b86eb7462996f3102d2b76562de3e56e5f13543a02486e1ee553cef18de")
		.roles("USER");
	}
	@Bean
	public PasswordEncoder passwordEncoder() {
		Pbkdf2PasswordEncoder passwordEncoder = new Pbkdf2PasswordEncoder();
		return passwordEncoder;
	}	
        ------
} 
Find the bean to create in XML configuration and assign it using <password-encoder> element.
<beans:bean name="passwordEncoder" 
          class="org.springframework.security.crypto.password.Pbkdf2PasswordEncoder"/> 

SCryptPasswordEncoder

The SCryptPasswordEncoder is the implementation of PasswordEncoder interface that uses SCrypt hashing function. To instantiate SCryptPasswordEncoder, we can optionally pass CPU cost, memory cost, parallelization parameter, key length for algorithm and salt length. To use SCryptPasswordEncoder, we need to resolve Bouncy castle dependency. Find the example to encode and match password using SCryptPasswordEncoder.
SCryptPasswordEncoder encoder = new SCryptPasswordEncoder();
String result = encoder.encode("ram123");
assertTrue(encoder.matches("ram123", result)); 
To use SCryptPasswordEncoder in Spring Security Java configuration, just create its bean.
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
	@Override
	protected void configure(AuthenticationManagerBuilder auth) throws Exception {
		auth.inMemoryAuthentication()
		.withUser("shatru")
		.password("$e0801$VoXA+5ot9fXbuLaOj5izqgd3JrOtBjvsqpbwssgHIA3omPpNB8t+FYtnsVpxx5DJjAvFtrA/klDstepB320TSw==$CLsjnAxzzNof2jkDrSBeoCTvuxO8qi67FRDY0vrCZwY=")
		.roles("USER");
	}
	@Bean
	public PasswordEncoder passwordEncoder() {
		SCryptPasswordEncoder passwordEncoder = new SCryptPasswordEncoder();
		return passwordEncoder;
	}	
        ------
} 
Find the bean to create in XML configuration and assign it using <password-encoder> element.
<beans:bean name="passwordEncoder" 
          class="org.springframework.security.crypto.scrypt.SCryptPasswordEncoder"/> 

DelegatingPasswordEncoder

The DelegatingPasswordEncoder is the default password encoder in Spring Security 5.0. The DelegatingPasswordEncoder delegates to another PasswordEncoder based upon the prefixed identifier. For example,
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.

Find a complete example.
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");
	}
} 
Find the equivalent XML configuration.
security-config.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/security"
	xmlns:beans="http://www.springframework.org/schema/beans" 
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
	http://www.springframework.org/schema/beans/spring-beans.xsd
	http://www.springframework.org/schema/security
	http://www.springframework.org/schema/security/spring-security.xsd">
	<http>
		<intercept-url  pattern="/**" access="hasRole('USER')" />
		<form-login /> 
	</http>
	<authentication-manager>
		<authentication-provider>
		    <user-service>
		        <user name="ram"
		             password="{noop}ram123" 
		             authorities="ROLE_USER" />
		        <user name="lakhan"
		             password="{bcrypt}$2a$10$q5pHs1fyVDbQSnBu3Il/meAONlMYFT1RhGlT2OC6IXX5.bp2JBZU6" 
		             authorities="ROLE_USER" />			        		        
		    </user-service>	
		</authentication-provider>
	</authentication-manager>
</beans:beans> 
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>
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-test</artifactId>
	</dependency>
	<dependency>
		<groupId>org.junit.jupiter</groupId>
		<artifactId>junit-jupiter-api</artifactId>
		<version>5.3.2</version>
		<scope>test</scope>
	</dependency>
	<dependency>
		<groupId>org.junit.jupiter</groupId>
		<artifactId>junit-jupiter-engine</artifactId>
		<version>5.3.2</version>
		<scope>test</scope>
	</dependency>
	<dependency>
		<groupId>org.junit.jupiter</groupId>
		<artifactId>junit-jupiter-params</artifactId>
		<version>5.3.2</version>
		<scope>test</scope>
	</dependency>
	<dependency>
		<groupId>org.junit.platform</groupId>
		<artifactId>junit-platform-launcher</artifactId>
		<version>1.3.2</version>
		<scope>test</scope>
	</dependency>
	<dependency>
		<groupId>org.bouncycastle</groupId>
		<artifactId>bcprov-jdk15on</artifactId>
		<version>1.64</version>
	</dependency>
</dependencies> 
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 Password Encoding
We will see welcome page as following.
Spring Security Password Encoding

Reference

Spring Security Reference

Download Source Code

POSTED BY
ARVIND RAI
ARVIND RAI
LEARN MORE








©2024 concretepage.com | Privacy Policy | Contact Us