Spring Security加密与哈希:BCrypt与PasswordEncoder
请详细解释Spring Security中的密码加密和哈希机制。BCrypt是什么?为什么推荐BCrypt而不是MD5/SHA-256?PasswordEncoder接口有哪些实现(BCryptPasswordEncoder/SCryptPasswordEncoder/Argon2PasswordEncoder)?如何自定义PasswordEncoder?什么是盐值(Salt)和工作因子(Strength)?
回答
古法程序员
为什么不能用MD5/SHA-256
| 算法 | 特点 | 安全性 |
|---|---|---|
| MD5 | 128位,速度快 | ⚠️ 已被碰撞攻击攻破 |
| SHA-256 | 256位,速度快 | ⚠️ 抗碰撞强但易被彩虹表/GPU暴力破解 |
| BCrypt | 慢哈希 + 盐值 | ✅ 故意慢,抗GPU |
| Argon2 | 内存硬 + CPU硬 | ✅ 最安全,2015年密码哈希竞赛冠军 |
核心问题:MD5/SHA256设计目标是快速计算,这使得攻击者可以用GPU每秒计算数十亿次哈希。密码哈希需要慢(计算成本高)。
BCrypt原理
BCrypt = Blowfish加密算法的密码哈希变体,核心特性:
- 盐值(Salt):每个密码用随机盐值,同一密码不同用户哈希不同
- 工作因子(Strength):迭代次数=2^strength,默认10(约70ms),可调整
- 抗GPU:需要大量内存,不适合GPU并行
$2a$10$N9qo8uLOickgx2ZMRZoMyeIjZAgcfl7p92ldGxad68LJZdL17lhWy
│ │ │ │
│ │ strength=10 hash值
│ 算法版本
PasswordEncoder实现
// BCrypt(推荐,Spring Security默认)
PasswordEncoder encoder = new BCryptPasswordEncoder(10); // strength默认10
String hash = encoder.encode("mypassword");
encoder.matches("mypassword", hash); // true
// SCrypt(内存硬)
PasswordEncoder encoder = new SCryptPasswordEncoder(
16384, // CPU成本
8, // 内存成本
1, // 并行度
32, // 密钥长度
16 // 盐值长度
);
// Argon2(内存+CPU硬,最安全)
PasswordEncoder encoder = new Argon2PasswordEncoder(
16, // 盐值长度
32, // 哈希长度
1, // 并行度
65536, // 内存成本(KB)
3 // 迭代次数
);
// DelegatingPasswordEncoder(支持多种编码格式,推荐用于迁移)
String encoded = "{bcrypt}$2a$10$...";
最佳实践
- 绝不存储明文密码
- 使用BCrypt或更安全的Argon2
- 工作因子随硬件提升而增加:每年增加strength(1)
- 密码传输用HTTPS(哈希在服务端做,不在客户端做)
- 支持密码策略(长度、复杂度、历史密码)
- 密码编码前缀:
{bcrypt}前缀可支持多种编码格式并存(迁移用)