CodeWalk

Spring Security加密与哈希:BCrypt与PasswordEncoder

作者:古法程序员 · 2026-05-30 12:55

请详细解释Spring Security中的密码加密和哈希机制。BCrypt是什么?为什么推荐BCrypt而不是MD5/SHA-256?PasswordEncoder接口有哪些实现(BCryptPasswordEncoder/SCryptPasswordEncoder/Argon2PasswordEncoder)?如何自定义PasswordEncoder?什么是盐值(Salt)和工作因子(Strength)?

回答

古法程序员

为什么不能用MD5/SHA-256

算法特点安全性
MD5128位,速度快⚠️ 已被碰撞攻击攻破
SHA-256256位,速度快⚠️ 抗碰撞强但易被彩虹表/GPU暴力破解
BCrypt慢哈希 + 盐值✅ 故意慢,抗GPU
Argon2内存硬 + CPU硬✅ 最安全,2015年密码哈希竞赛冠军

核心问题:MD5/SHA256设计目标是快速计算,这使得攻击者可以用GPU每秒计算数十亿次哈希。密码哈希需要(计算成本高)。

BCrypt原理

BCrypt = Blowfish加密算法的密码哈希变体,核心特性:

  1. 盐值(Salt):每个密码用随机盐值,同一密码不同用户哈希不同
  2. 工作因子(Strength):迭代次数=2^strength,默认10(约70ms),可调整
  3. 抗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$...";

最佳实践

  1. 绝不存储明文密码
  2. 使用BCrypt或更安全的Argon2
  3. 工作因子随硬件提升而增加:每年增加strength(1)
  4. 密码传输用HTTPS(哈希在服务端做,不在客户端做)
  5. 支持密码策略(长度、复杂度、历史密码)
  6. 密码编码前缀{bcrypt}前缀可支持多种编码格式并存(迁移用)