DelegatingPasswordEncoder 入门
大约 4 分钟
DelegatingPasswordEncoder
入门
1、基本概念
DelegatingPasswordEncoder
的核心职责是根据存储在密码中的编码器标识符(即编码算法的前缀)来委托给不同的密码编码器来处理密码的编码和验证。
DelegatingPasswordEncoder
是 Spring Security 中一个非常强大的工具,能够支持多种密码编码算法,并通过前缀来识别不同的编码方式。在实际应用中,它使得密码策略的切换和迁移更加灵活,同时也增强了安全性。通过合理配置,可以确保系统在不同的安全需求下始终保持灵活性和兼容性。
2、组成
DelegatingPasswordEncoder
是一种组合型的密码编码器,它由多个密码编码器(PasswordEncoder
)组成。它会根据密码字符串中的前缀来选择合适的编码器进行处理。密码编码器前缀通常采用算法的名称,后跟密码哈希值。例如,bcrypt
、pbkdf2
、scrypt
等。
主要构件:
- 编码器映射(
passwordEncoders
):一个映射,关联了密码编码器的标识符(如bcrypt
)与相应的编码器实现(如BCryptPasswordEncoder
)。 - 前缀解析:密码字符串通常以
{算法}
的格式存储,DelegatingPasswordEncoder
通过解析这个前缀来选择正确的编码器。例如,{bcrypt}$2a$10$...
就会选择BCryptPasswordEncoder
进行验证。 - 默认编码器:如果没有找到合适的编码器,
DelegatingPasswordEncoder
会回退到一个默认的编码器(通常是一个更强的算法,如bcrypt
或pbkdf2
)。
3、配置
Spring Security 使用 DelegatingPasswordEncoder
时,需要指定一个默认的密码编码器,并且将其配置为管理密码编码策略
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.DelegatingPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.HashMap;
import java.util.Map;
@Configuration
public class SecurityConfig {
@Bean
public PasswordEncoder passwordEncoder() {
String idForEncode = "bcrypt"; // 默认使用 bcrypt 编码器
Map<String, PasswordEncoder> encoders = new HashMap<>();
encoders.put("bcrypt", new BCryptPasswordEncoder());
encoders.put("pbkdf2", new Pbkdf2PasswordEncoder());
encoders.put("scrypt", new SCryptPasswordEncoder());
encoders.put("argon2", new Argon2PasswordEncoder());
DelegatingPasswordEncoder delegatingPasswordEncoder = new DelegatingPasswordEncoder(idForEncode, encoders);
return delegatingPasswordEncoder;
}
}
说明:
DelegatingPasswordEncoder
的构造函数:接收一个默认编码器的标识符(如bcrypt
)和编码器的映射。在此例中,我们为不同的密码编码算法提供了PasswordEncoder
实现。- 默认编码器:
idForEncode
指定了默认使用的编码器,如果密码没有指定编码前缀,系统就会使用该默认编码器。
4、工作流程
DelegatingPasswordEncoder
的工作流程主要有以下几个步骤:
- 密码存储:
- 当用户首次注册或更新密码时,
DelegatingPasswordEncoder
会根据配置的编码器策略来选择适当的编码器(例如,bcrypt
)。 - 生成的密码将以
{bcrypt}
前缀的形式存储到数据库中。例如,密码可能存储为{bcrypt}$2a$10$...
。
- 当用户首次注册或更新密码时,
- 密码验证:
- 当用户登录时,
DelegatingPasswordEncoder
会从存储的密码中提取出前缀(例如{bcrypt}
)并确定所使用的编码算法。 - 它会根据提取的前缀来选择合适的
PasswordEncoder
来进行密码验证。 - 如果密码匹配,则验证通过;否则验证失败。
- 当用户登录时,
- 密码迁移:
- 如果系统在运行时需要切换到一个更强的密码编码算法(例如从
bcrypt
切换到argon2
),DelegatingPasswordEncoder
可以支持这种迁移。 - 在这种情况下,存储的密码会包含旧的编码算法(如
{bcrypt}
),而新注册的用户或密码更新后的用户会使用新的编码算法(如{argon2}
)。 - 用户登录时,
DelegatingPasswordEncoder
会根据当前的编码算法来验证密码,如果验证成功,它会提示更新密码,并使用新算法重新编码并存储。
- 如果系统在运行时需要切换到一个更强的密码编码算法(例如从
5、优势
- 支持多种密码编码算法:可以同时支持多个密码编码算法,并且可以根据需求灵活配置。
- 兼容性:当系统需要迁移到更强的加密算法时,
DelegatingPasswordEncoder
可以处理既存的用户密码,而不会影响系统的兼容性。 - 密码迁移:通过在
DelegatingPasswordEncoder
配置中使用默认编码器,可以方便地对密码进行迁移。
6、示例
@Test
public void demo01() {
// 定义支持的密码编码器
Map<String, PasswordEncoder> encoders = new HashMap<>();
encoders.put("bcrypt", new BCryptPasswordEncoder());
// 假设 passwordEncoder 是一个已经配置好的 DelegatingPasswordEncoder
PasswordEncoder passwordEncoder = new DelegatingPasswordEncoder("bcrypt", encoders);
// 编码密码
String encodedPassword = passwordEncoder.encode("mySecretPassword");
// 验证密码
boolean matches = passwordEncoder.matches("mySecretPassword", encodedPassword);
// 输出验证结果
System.out.println("Encoded password: " + encodedPassword);
System.out.println("Password matches: " + matches);
// 测试失败情况
boolean mismatch = passwordEncoder.matches("wrongPassword", encodedPassword);
System.out.println("Password matches with wrong input: " + mismatch);
}