1. 概述

在用户注册流程中,密码编码是一个非常关键的环节。其核心目的就是不以明文形式存储密码,从而避免敏感信息泄露。

Spring Security 提供了多种密码编码机制,本文我们将以 BCryptPasswordEncoder 为例,介绍其使用方式。BCrypt 是目前业界推荐的安全编码方式,相比其他如 MD5PasswordEncoder、ShaPasswordEncoder 等算法更安全,且这些老算法目前已被弃用

2. 定义密码编码器

首先,我们通过配置类定义一个 BCryptPasswordEncoder Bean:

@Bean
public PasswordEncoder encoder() {
    return new BCryptPasswordEncoder();
}

与早期的编码器(如 SHAPasswordEncoder)不同的是,BCrypt 会自动内部生成随机 salt,无需手动传入。这意味着每次编码的结果都会不同,但验证时依然能正确匹配。

BCrypt 会将 salt 值存储在 hash 字符串中。例如下面这个 hash 值:

$2a$10$ZLhnHxdpHETcxmtEStgpI./Ri1mksgJ9iDP36FmfMdYyVg9g0b2dq

通过 $ 分隔的三部分含义如下:

  1. 2a:BCrypt 算法版本
  2. 10:算法强度(log2(rounds))
  3. ZLhnHxdpHETcxmtEStgpI.:前22位是 salt 值,后面是加密后的 hash

⚠️ 注意:BCrypt 输出的字符串长度为 60,因此数据库字段长度必须大于等于 60,否则在验证时会抛出 Invalid Username or Password 异常。

3. 用户注册时进行密码编码

在用户注册时,我们需要对输入的明文密码进行编码。通常在 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);
}

✅ 要点:

  • 使用 passwordEncoder.encode() 对密码进行编码
  • 注册阶段只需要编码一次即可
  • 数据库存储字段长度要足够(建议 VARCHAR(60) 以上)

4. 用户认证时使用编码器

除了注册阶段,我们在用户登录时也需要使用相同的编码器进行密码验证。

首先,在配置类中注入我们之前定义的 passwordEncoder:

@Autowired
private UserDetailsService userDetailsService;

@Bean
public DaoAuthenticationProvider authProvider() {
    DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
    authProvider.setUserDetailsService(userDetailsService);
    authProvider.setPasswordEncoder(encoder());
    return authProvider;
}

然后在安全配置中引用这个认证提供者:

XML 配置方式:

<authentication-manager>
    <authentication-provider ref="authProvider" />
</authentication-manager>

Java 配置方式:

@Configuration
@ComponentScan(basePackages = { "com.baeldung.security" })
@EnableWebSecurity
public class SecSecurityConfig {

    @Bean
    public AuthenticationManager authManager(HttpSecurity http) throws Exception {
        return http.getSharedObject(AuthenticationManagerBuilder.class)
            .authenticationProvider(authProvider())
            .build();
    }
    
    ...
}

✅ 要点:

  • 认证流程中必须使用相同的 PasswordEncoder 实例
  • 不要遗漏配置中的 authentication-provider 设置
  • 确保 UserDetailsService 实现类正确加载用户信息

5. 小结

本文介绍了在 Spring Security 中如何使用 BCryptPasswordEncoder 对用户密码进行安全编码,包括注册时的加密处理和认证时的密码验证流程。

BCrypt 的优势在于:

  • 自动生成随机 salt
  • 每次加密结果不同,但不影响验证
  • 算法安全性高,适合现代 Web 应用

完整示例代码可在 GitHub 上找到:Spring Security 注册流程完整示例


原始标题:Password Encoding with Spring | Baeldung