Learn Spring Security
Contents
Password Encoder
In the last chapter, we mentioned that we have not encrypted the password for any users in the database. You might also have noticed from the ListStudents API response that the passwords are all just plain text.
Storing passwords in plain text is a terrible idea that any level of database compromise will expose your application users. It could be logging the passwords by mistake or inadequately securing the database dumps etc., We must encrypt the password using irreversible cryptographic functions that even the system / database administrators could not retrieve them.
In this chapter, we will introduce the most commonly used BCryptPasswordEncoder offered by Spring Security.
BCryptPasswordEncoder
BCryptPasswordEncoder is one of the implementations of the PasswordEncoder interface. It helps us to encode the password as well as match the raw password with the encoded password on authentication.
Let's create and register BCryptPasswordEncoder bean in the SecurityBean class.
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
Encrypt the passwords
Autowire the PasswordEncoder in AppDataInitialiser, and then use it to encrypt the password for all the users before saving it in the database like below:
AppUser gruUser = AppUser.builder()
.username("Gru")
.password(passwordEncoder.encode("password"))
.email("gru@email.com")
.roles(Collections.singleton(instructorRole))
.build();
Restarting the application triggers AppDataInitialiser to clear the database and recreate all the user records with encrypted passwords.
Update UserDetails
In order to let Spring Security know that we have used BCryptPasswordEncoder to encrypt the passwords, we might think to replace the prefix for each password from {noop} to {bcrypt}. But it is no longer required as Spring Security is already aware of the PasswordEncoder bean available in the ApplicationContext. Let's remove the prefix and map the password directly while creating the UserDetails list in DbUserDetailsService.
public List<UserDetails> getAllUserDetails() {
return appUserRepository.findAll()
.stream()
.map(appUser -> User.builder()
.username(appUser.getUsername())
.password(appUser.getPassword())
.authorities(Collections.EMPTY_SET)
.build()
)
.collect(Collectors.toList());
}
In addition to encrypting the passwords, BCryptPasswordEncoder bean will be automatically used by Spring Security for password verification also using matches() method.
Restart the application and send a GET request to the ListStudents API from Postman as Bob user. You can see from the response that all the passwords are encrypted now.
Though the passwords are encrypted, it is still not a good practice to expose them in any manner. Let's add @JsonIgnore annotation to the password field in AppUser entity to ignore serializing it.