跳到正文
Joeplover
后端开发·2026-01-21·约 3 分钟阅读

BCrypt 密码加密:Spring Security 工具类

使用 Spring Security BCryptPasswordEncoder 生成和验证密码哈希的实用工具。

密码安全

BCrypt 密码加密:为什么不能用 MD5

背景

在 CheckByAI 项目中,我需要在数据库中存储用户密码。初学时最自然的想法是用 MD5 哈希——一句 DigestUtils.md5DigestAsHex() 就搞定了。但 MD5 设计目标就是快——GPU 每秒可以计算数十亿次 MD5,暴力破解成本极低。

MD5 为什么不适合存密码

// 这样存储是不安全的
String hashed = DigestUtils.md5DigestAsHex(password.getBytes());

两个致命问题:

  1. 速度快:现代 GPU 每秒可算 200 亿次 MD5,8 位纯数字密码 0.1 秒破解
  2. 无盐值:同样的密码生成同样的哈希,彩虹表可以逆向查询

加盐(salt)可以解决彩虹表问题,但盐值需要单独存储和管理,自己实现的盐值管理容易出错。

BCrypt 的设计原理

BCrypt 专门为密码存储设计,核心设计理念是慢:

@Bean
public PasswordEncoder passwordEncoder() {
    return new BCryptPasswordEncoder();  // strength 默认 10,即 2^10 轮
}

工作流程

  1. 自动生成随机盐值(22 个字符,嵌入在结果字符串中)
  2. 执行 2^strength 轮 Blowfish 加密(默认 2^10 = 1024 轮)
  3. 输出格式:$2a$10$[22位盐值][31位哈希值]
// 每次加密结果不同
encoder.encode("admin123")
// $2a$10$N9qo8uLOickgx2ZMRZoMyeIjZAgcfl7p92ldGxad68LJZdL17lhWy
// $2a$10$dQZrRjGiqQjX5nLjKjKJk.vb5iSjZn4qGysKLzGXJSjK.d5a8X5uS
// 两次 encode 结果不同,但 matches() 验证都能通过

强度(strength)参数

// strength=4 最低 16 轮,测试用
// strength=10 默认 1024 轮,约 100ms
// strength=12 4096 轮,约 400ms
new BCryptPasswordEncoder(10)

strength 每 +1,破解时间翻倍。摩尔定律带来的算力提升可以被 strength 参数抵消——2024 年的 strength=10 和 2014 年的 strength=10 都是约 100ms。

生产实践

在 CheckByAI 项目中,用 BCrypt 替代了之前的 MD5:

@Service
public class AuthService {
    @Autowired
    private PasswordEncoder passwordEncoder;
    @Autowired
    private UserRepository userRepository;
    
    public User register(String username, String password) {
        User user = new User();
        user.setUsername(username);
        user.setPassword(passwordEncoder.encode(password)); // BCrypt 加密
        return userRepository.save(user);
    }
    
    public boolean login(String username, String password) {
        User user = userRepository.findByUsername(username);
        return user != null && passwordEncoder.matches(password, user.getPassword());
    }
}

Spring Security 的 PasswordEncoder 接口设计得很好——加密用 encode(),验证用 matches()。切换算法只需要换实现类,业务代码不受影响。

替代方案

如果 BCrypt 的性能还不能满足需求(比如高并发登录场景),可以考虑:

  • SCrypt:内存密集型的慢哈希,ASIC 和 GPU 加速效果有限
  • Argon2:BCrypt 的现代替代,2023 年密码哈希竞赛胜出者
  • PBKDF2:如果项目必须用 JDK 内置算法(不需要额外依赖)

但在大部分场景下,BCrypt 已经足够好。不要为了微乎其微的性能差异牺牲安全性。