PasswordEncoder 入门
PasswordEncoder 入门
1、PasswordEncoder

2、BCryptPasswordEncoder
BCryptPasswordEncoder
使用的是基于 bcrypt 算法的加密方式,它是一种适用于密码存储的哈希算法,能够有效防止彩虹表攻击(rainbow table attack)和暴力破解攻击。
通过合理设置工作因子,可以在保证加密强度的同时,优化系统的性能。
BCrypt
算法简介
2.1、BCrypt 是一种基于 Blowfish 加密算法 的密钥派生函数(key derivation function),设计上具有抗时间延迟攻击(例如,暴力破解)。BCrypt 是为密码存储和验证而设计的,其主要特点包括:
- 盐值(Salt):每次对密码加密时都会生成一个随机的盐值。盐值与密码一起进行加密,确保即使用户密码相同,存储在数据库中的哈希值也不相同。
- 工作因子(Work Factor):工作因子控制哈希计算的迭代次数,增加工作因子可以提高破解的难度(需要更多的计算资源)。BCrypt 默认的工作因子是 10。
2.2、BCryptPasswordEncoder 源码
public class BCryptPasswordEncoder implements PasswordEncoder {
private Pattern BCRYPT_PATTERN = Pattern.compile("\\A\\$2(a|y|b)?\\$(\\d\\d)\\$[./0-9A-Za-z]{53}");
private final int strength;
private final BCryptVersion version;
private final SecureRandom random;
...
}
构造方法
public BCryptPasswordEncoder() {
this(-1);
}
...
public BCryptPasswordEncoder(BCryptVersion version, int strength, SecureRandom random) {
if (strength != -1 && (strength < BCrypt.MIN_LOG_ROUNDS || strength > BCrypt.MAX_LOG_ROUNDS)) {
throw new IllegalArgumentException("Bad strength");
}
this.version = version;
this.strength = (strength == -1) ? 10 : strength;
this.random = random;
}
BCrypt 部分源码
static final int MIN_LOG_ROUNDS = 4;
static final int MAX_LOG_ROUNDS = 31;
从构造方法可以看出:
- 默认的工作因子是:10
- 最大工作因子是:31
- 最小默认因子是:4
源码路径
org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder
2.3、常用方法
encode
@Override
public String encode(CharSequence rawPassword) {
if (rawPassword == null) {
throw new IllegalArgumentException("rawPassword cannot be null");
}
String salt = getSalt();
return BCrypt.hashpw(rawPassword.toString(), salt);
}
这个方法用于对明文密码进行加密,并返回加密后的密码字符串。加密后的密码将包括盐值,形成一个可以直接存储在数据库中的字符串。
示例如下:
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
String encodedPassword = encoder.encode("Jin");
matches
@Override
public boolean matches(CharSequence rawPassword, String encodedPassword) {
if (rawPassword == null) {
throw new IllegalArgumentException("rawPassword cannot be null");
}
if (encodedPassword == null || encodedPassword.length() == 0) {
this.logger.warn("Empty encoded password");
return false;
}
if (!this.BCRYPT_PATTERN.matcher(encodedPassword).matches()) {
this.logger.warn("Encoded password does not look like BCrypt");
return false;
}
return BCrypt.checkpw(rawPassword.toString(), encodedPassword);
}
该方法用于验证用户输入的密码是否与存储的加密密码匹配。它会自动提取加密字符串中的盐值,使用相同的加密算法重新加密输入的密码并进行比较。
示例如下:
boolean isMatch = encoder.matches("Jin", encodedPassword);
2.4、工作因子(Strength)
工作因子(Strength)控制 BCrypt 算法的计算强度。它影响加密速度,并且直接影响计算的安全性。工作因子值通常是一个整数,表示对密码进行的迭代次数的对数(2 的幂次方)
- Strength = 10:默认值,意味着进行 2^10(1024)次加密运算。
- Strength = 12:意味着进行 2^12(4096)次计算,增加了加密的强度。
- Strength = 14:意味着进行 2^14(16384)次计算,安全性更高,但会使加密和验证过程更加耗时。
一般来说,选择一个平衡的值(例如,10 到 12 之间)通常足够,但为了提升安全性,可以适当提高工作因子。
2.5、加密过程的细节
当调用 BCryptPasswordEncoder.encode()
方法时,它会执行以下步骤:
生成一个随机盐值(Salt)。
使用盐值和明文密码进行多次加密运算。
返回一个包含盐值和加密密码的字符串,格式通常是:
$2a$10$hq/W3SU5CvqiuZqYM5WiD.1eLNv8MURsySuTSCyqJIikH1CxnXMNa
其中:
$2a$
:表示使用的是 bcrypt 算法。10
:表示工作因子(Strength)。- 后续的字符串是盐值和加密后的密码。
2.6、验证过程的细节
当调用 BCryptPasswordEncoder.matches()
方法时,它会执行以下步骤:
- 从存储的加密密码中提取盐值。
- 使用该盐值对输入的明文密码进行加密。
- 比较生成的哈希值与存储的哈希值是否相同。
由于 BCrypt 算法在加密时使用了盐值,确保每个用户的密码即使相同,其哈希值也不一样。因此,即使密码相同,存储在数据库中的加密结果也是不同的。
BCryptPasswordEncoder
和 Spring Security
配合
2.7、在 Spring Security 中,通常将 BCryptPasswordEncoder
与 AuthenticationManager
配合使用,以便在用户登录时验证密码。
例如,在使用 InMemoryAuthentication
时,你可以这样配置 BCryptPasswordEncoder
:
3、Argon2PasswordEncoder
使用Argon2算法对密码进行哈希处理。Argon2是密码哈希比赛的获胜者。为了防止在自定义硬件上进行密码破解,Argon2是一种故意缓慢的算法,需要大量内存。与其他自适应单向函数一样,它应该在您的系统上调整为大约1秒来验证一个密码。当前的Argon2PasswordEncoder
实现需要使用BouncyCastle
库。
Pbkdf2PasswordEncoder
使用PBKDF2算法对密码进行哈希处理。为了防止密码破解,PBKDF2是一种故意缓慢的算法。与其他自适应单向函数一样,它应该在您的系统上调整为大约1秒来验证一个密码。当需要FIPS认证时,这种算法是一个很好的选择。
SCryptPasswordEncoder
使用scrypt算法对密码进行哈希处理。为了防止在自定义硬件上进行密码破解,scrypt是一种故意缓慢的算法,需要大量内存。与其他自适应单向函数一样,它应该在您的系统上调整为大约1秒来验证一个密码。
DelegatingPasswordEncoder
- 表中存储的密码形式:
{bcrypt}
$2a$10$GRLdNijSQMUvl/au9ofL.eDwmoohzzS7.rmNSJZ.0FxO/BTk76klW - 通过如下源码可以知道:可以通过
{bcrypt}
前缀动态获取和密码的形式类型一致的PasswordEncoder对象 - 目的:方便随时做密码策略的升级,兼容数据库中的老版本密码策略生成的密码