1. Overview
1.概述
In this tutorial, we’ll discuss a critical part of the registration process, password encoding, which is basically not storing the password in plaintext.
在本教程中,我们将讨论注册过程中的一个关键部分,密码编码,这基本上是不以明文存储密码。
There are a few encoding mechanisms supported by Spring Security, and for this tutorial, we’ll use BCrypt, as it’s usually the best solution available.
Spring Security支持几种编码机制,在本教程中,我们将使用BCrypt,因为它通常是现有的最佳解决方案。
Most of the other mechanisms, such as the MD5PasswordEncoder and ShaPasswordEncoder, use weaker algorithms and are now deprecated.
大多数其他机制,如MD5PasswordEncoder和ShaPasswordEncoder,使用较弱的算法,现在已被废弃。
2. Define the Password Encoder
2.定义密码编码器
We’ll start by defining the simple BCryptPasswordEncoder as a bean in our configuration:
我们将首先把简单的BCryptPasswordEncoder定义为配置中的一个Bean。
@Bean
public PasswordEncoder encoder() {
return new BCryptPasswordEncoder();
}
Older implementations, such as SHAPasswordEncoder, require the client to pass in a salt value when encoding the password.
较早的实现,如SHAPasswordEncoder,要求客户端在对密码进行编码时传入一个盐值。
BCrypt, however, will internally generate a random salt instead. This is important to understand because it means that each call will have a different result, so we only need to encode the password once.
然而,BCrypt,将在内部生成一个随机盐,而不是。这一点很重要,因为这意味着每次调用都会有不同的结果,所以我们只需要对密码进行一次编码。
To make this random salt generation work, BCrypt will store the salt inside the hash value itself. For instance, the following hash value:
为了使这种随机盐生成发挥作用,BCrypt将把盐存储在哈希值本身中。例如,下面的哈希值。
$2a$10$ZLhnHxdpHETcxmtEStgpI./Ri1mksgJ9iDP36FmfMdYyVg9g0b2dq
Separates three fields by $:
用$分隔三个字段。
- The “2a” represents the BCrypt algorithm version
- The “10” represents the strength of the algorithm
- The “ZLhnHxdpHETcxmtEStgpI.” part is actually the randomly generated salt. Basically, the first 22 characters are salt. The remaining part of the last field is the actual hashed version of the plain text.
Also, be aware that the BCrypt algorithm generates a String of length 60, so we need to make sure that the password will be stored in a column that can accommodate it. A common mistake is to create a column of a different length, and then get an Invalid Username or Password error at authentication time.
另外,请注意BCrypt算法会生成一个长度为60的字符串,所以我们需要确保密码将被存储在一个能够容纳它的列中。一个常见的错误是创建一个不同长度的列,然后在认证时得到一个无效的用户名或密码错误。
3. Encode the Password on Registration
3.在注册时对密码进行编码
We’ll use the PasswordEncoder in our UserService to hash the password during the user registration process:
我们将在我们的UserService中使用PasswordEncoder,在用户注册过程中对密码进行散列。
Example 3.1. The UserService Hashes the Password
示例 3.1.UserService 加密密码。
@Autowired
private PasswordEncoder passwordEncoder;
@Override
public User registerNewUserAccount(UserDto accountDto) throws EmailExistsException {
if (emailExist(accountDto.getEmail())) {
throw new EmailExistsException(
"There is an account with that email adress:" + accountDto.getEmail());
}
User user = new User();
user.setFirstName(accountDto.getFirstName());
user.setLastName(accountDto.getLastName());
user.setPassword(passwordEncoder.encode(accountDto.getPassword()));
user.setEmail(accountDto.getEmail());
user.setRole(new Role(Integer.valueOf(1), user));
return repository.save(user);
}
4. Encode the Password on Authentication
4.对认证时的密码进行编码
Now we’ll handle the other half of this process and encode the password when the user authenticates.
现在我们将处理这个过程的另一半,在用户验证时对密码进行编码。
First, we need to inject the password encoder bean we defined earlier into our authentication provider:
首先,我们需要将之前定义的密码编码器bean注入我们的认证提供者。
@Autowired
private UserDetailsService userDetailsService;
@Bean
public DaoAuthenticationProvider authProvider() {
DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
authProvider.setUserDetailsService(userDetailsService);
authProvider.setPasswordEncoder(encoder());
return authProvider;
}
The security configuration is simple:
安全配置很简单。
- we inject our implementation of the users details service
- we define an authentication provider that references our details service
- we also enable the password encoder
Finally, we need to reference this auth provider in our security XML configuration:
最后,我们需要在我们的安全XML配置中引用这个认证提供者。
<authentication-manager>
<authentication-provider ref="authProvider" />
</authentication-manager>
Or, if we’re using Java configuration:
或者,如果我们使用的是Java配置。
@Configuration
@ComponentScan(basePackages = { "com.baeldung.security" })
@EnableWebSecurity
public class SecSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authProvider());
}
...
}
5. Conclusion
5.结论
This brief article continues the Registration series by showing how to properly store the password in the database by leveraging the simple, but very powerful, BCrypt implementation.
这篇简短的文章继续注册系列,展示了如何利用简单但非常强大的BCrypt实现在数据库中正确存储密码。
The full implementation of this Registration with Spring Security article can be found over on GitHub.
使用 Spring Security 进行注册的完整实现文章可以在 GitHub 上找到over on GitHub。