🏠Spring FrameworkPassword Encoder in Spring Security

Password Encoder in Spring Security

Spring Framework

In this post, We will take a look at password encoders in detail with an example. Traditionally, storing passwords were hard. The application will have to encode user passwords and store them in a database.

But with password encoders provided by spring security, all of these can be done automatically. Password Encoders are beans that transform plain text password into hashes. As the hashes cannot be reversed into plaintext, it is a secure way to store passwords.

Password Hashing

To begin with, Hashing algorithms take a sequence of bytes and turn into a unique fixed-length hash string.

Hashing algorithms are one-way functions and cannot be reversed. This means the original plain text cannot be generated back from a hash.

This property makes the hashing viable for storing passwords.

Spring Security Password Encoder

For the password encoding/hashing, Spring Security expects a password encoder implementation. Also, it provides dogmatic implementations based on industry standards. These encoders will be used in the password storing phases and validation phase of authentication.

The passwordEncoders have two main tasks. They are,

  1. encoder.encode(String rawPassword) – converts a given plaintext password into an encoded password. And how it converts is up to the implementation. This part happens at the time when the password is stored in the DB. Usually when registering a user or changing the password.
  2. encoder.matches(rawPassword, encodedPassword) – Used whenever login happens. Security context will load the encrypted password from the database and use this method to compare against the raw password.

Here is how the encoders play role in the registration process.

The following diagram illustrates how Spring Security uses encoder for validating the login password.

now let’s see some examples.

Registration with password encoders

At the time of registration, you need to encode the password before storing it in the database. For this, you need to define a bean of type PasswordEncoder. At the time of writing the best implementation is to use the BCrypt algorithm. Here is how you can do it.

@Bean PasswordEncoder passwordEncoder(){ return new BCryptPasswordEncoder(); }
Code language: JavaScript (javascript)

And somewhere in your registration API endpoint, you will have to autowire this bean. Here is a simple example.

@RestController public class RegistrationController { private final UserAccountRepository userAccountRepository; private final PasswordEncoder passwordEncoder; public UserController(UserAccountRepository userAccountRepository, PasswordEncoder passwordEncoder) { this.userAccountRepository = userAccountRepository; this.passwordEncoder = passwordEncoder; } @PostMapping("/register") public UserAccount register(@RequestParam("username") String username, @RequestParam("password") String password, @RequestParam("firstName") String firstName, @RequestParam("lastName") String lastName) { UserAccount userAccount = new UserAccount(); userAccount.setFirstName(firstName); userAccount.setLastName(lastName); userAccount.setUsername(username); userAccount.setActive(true); userAccount.setPassword(passwordEncoder.encode(password)); return userAccountRepository.save(userAccount); } }
Code language: PHP (php)

You can check out git repository for the full implementation at the end of this post. In this example we registered the user with the help of BCryptPasswordEncoder. You can see how the passwords had been encoded in the below picture.

Migrating passwords from Spring Security 4

If you are upgrading from spring security4, then you need to make sure that the current passwords are encrypted using the same algorithm. Here is a sample code to convert plaintext passwords to hashes.

public class BCryptConverter { public static void main(String[] args) { BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder(); System.out.println(bCryptPasswordEncoder.encode("Hello@123")); System.out.println(bCryptPasswordEncoder.encode("Hello#123")); } }
Code language: JavaScript (javascript)

Delegating Password Encoder

There may be situations if you wanted to use multiple types of encoders within the same data source. For example, MD5, SHA-256, pbkdf2 are some common password hashing functions. For this reason, spring offers a DelegatingPasswordEncoder

This encoder relies on other password encoders by routing the requests based on a password prefix. To use this, you need to make some changes to our previous arrangement.

You need to create the bean for type DelegatingPasswordEncoder instead of BCryptPasswordEncoder and you can do this easily with the help of PasswordEncoderFactories class.

@Bean PasswordEncoder passwordEncoder(){ return PasswordEncoderFactories.createDelegatingPasswordEncoder(); }
Code language: CSS (css)

This delegating encoder encodes with bcrypt algorithm by default. This is why the password stored in the database will be prepended with the text {bcrypt}. This prepended information will be used to identify the appropriate passwordEncoder when encoder.matches() method is called.

At the time of writing the default mapping for encoding type is as shown below.

EncodingIdImplementation to match
bcryptnew org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder();
ldapnew org.springframework.security.crypto.password.LdapShaPasswordEncoder();
MD4new org.springframework.security.crypto.password.Md4PasswordEncoder();
MD5new org.springframework.security.crypto.password.MessageDigestPasswordEncoder(“MD5”);
noopnew org.springframework.security.crypto.password.NoOpPasswordEncoder.getInstance();
pbkdf2new org.springframework.security.crypto.password.Pbkdf2PasswordEncoder();
scryptnew org.springframework.security.crypto.scrypt.SCryptPasswordEncoder();
SHA-1new org.springframework.security.crypto.password.MessageDigestPasswordEncoder(“SHA-1”);
SHA-256new org.springframework.security.crypto.password.MessageDigestPasswordEncoder(“SHA-256”);
sha256new org.springframework.security.crypto.password.StandardPasswordEncoder();
argon2new org.springframework.security.crypto.argon2.Argon2PasswordEncoder();

Customizing DelegatingPasswordEncoder

You can customize the list of supported encoding types by creating the DelegatingPasswordEncoder by your own. For example, The following would only support MD5, bcrypt and plainText(noop) encoding.

@Bean PasswordEncoder passwordEncoder() { Map<String, PasswordEncoder> encoders = new HashMap<>(); encoders.put("noop", NoOpPasswordEncoder.getInstance()); encoders.put("bcrypt", new BCryptPasswordEncoder()); encoders.put("MD5", new MessageDigestPasswordEncoder("MD5")); return new DelegatingPasswordEncoder("bcrypt", encoders); }
Code language: JavaScript (javascript)

Migrating old plaintext passwords to bcyrpt

The delegating encoder will allow both encoded and plain text passwords to co-exist. However, there is still a security risk for those passwords which are not encoded. In these situations, Write a program that will convert all plain text passwords to encoded strings. Here is a sample for you to try with.

public class BCryptConvert { public static void main(String[] args) { PasswordEncoder passwordEncoder = PasswordEncoderFactories.createDelegatingPasswordEncoder(); System.out.println(passwordEncoder.encode("Hello@123")); System.out.println(passwordEncoder.encode("Hello#123")); } }
Code language: JavaScript (javascript)

GitHub

You can find these examples at out GitHub repository.

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *