跳至主要內容

DelegatingPasswordEncoder 入门

Jin大约 4 分钟

DelegatingPasswordEncoder 入门

1、基本概念

DelegatingPasswordEncoder 的核心职责是根据存储在密码中的编码器标识符(即编码算法的前缀)来委托给不同的密码编码器来处理密码的编码和验证。

DelegatingPasswordEncoder 是 Spring Security 中一个非常强大的工具,能够支持多种密码编码算法,并通过前缀来识别不同的编码方式。在实际应用中,它使得密码策略的切换和迁移更加灵活,同时也增强了安全性。通过合理配置,可以确保系统在不同的安全需求下始终保持灵活性和兼容性。

2、组成

DelegatingPasswordEncoder 是一种组合型的密码编码器,它由多个密码编码器(PasswordEncoder)组成。它会根据密码字符串中的前缀来选择合适的编码器进行处理。密码编码器前缀通常采用算法的名称,后跟密码哈希值。例如,bcryptpbkdf2scrypt 等。

主要构件:

  • 编码器映射(passwordEncoders:一个映射,关联了密码编码器的标识符(如 bcrypt)与相应的编码器实现(如 BCryptPasswordEncoder)。
  • 前缀解析:密码字符串通常以 {算法} 的格式存储,DelegatingPasswordEncoder 通过解析这个前缀来选择正确的编码器。例如,{bcrypt}$2a$10$... 就会选择 BCryptPasswordEncoder 进行验证。
  • 默认编码器:如果没有找到合适的编码器,DelegatingPasswordEncoder 会回退到一个默认的编码器(通常是一个更强的算法,如 bcryptpbkdf2)。

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 的工作流程主要有以下几个步骤:

  1. 密码存储
    • 当用户首次注册或更新密码时,DelegatingPasswordEncoder 会根据配置的编码器策略来选择适当的编码器(例如,bcrypt)。
    • 生成的密码将以 {bcrypt} 前缀的形式存储到数据库中。例如,密码可能存储为 {bcrypt}$2a$10$...
  2. 密码验证
    • 当用户登录时,DelegatingPasswordEncoder 会从存储的密码中提取出前缀(例如 {bcrypt})并确定所使用的编码算法。
    • 它会根据提取的前缀来选择合适的 PasswordEncoder 来进行密码验证。
    • 如果密码匹配,则验证通过;否则验证失败。
  3. 密码迁移
    • 如果系统在运行时需要切换到一个更强的密码编码算法(例如从 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);
    }
贡献者: Jin