【问题标题】:How override the default BCryptPasswordEncoder created through PasswordEncoderFactories?如何覆盖通过 PasswordEncoderFactories 创建的默认 BCryptPasswordEncoder?
【发布时间】:2021-02-04 21:49:56
【问题描述】:

我知道在 Spring Security 中会出现以下情况:

There was an unexpected error (type=Internal Server Error, status=500).
There is no PasswordEncoder mapped for the id "null"
java.lang.IllegalArgumentException: There is no PasswordEncoder mapped for the id "null"

解决方案是定义一个PasswordEncoder。为简单起见,可以定义以下内容:

@Bean
PasswordEncoder encoder() {
    return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}

现在,在幕后,createDelegatingPasswordEncoder() 方法是如何定义的(到目前为止,它适用于 Spring Security 5.4.2)(有关更多详细信息,请参阅PasswordEncoderFactories 类):

@SuppressWarnings("deprecation")
public static PasswordEncoder createDelegatingPasswordEncoder() {
    String encodingId = "bcrypt";
    Map<String, PasswordEncoder> encoders = new HashMap<>();
    encoders.put(encodingId, new BCryptPasswordEncoder());
    encoders.put("ldap", new org.springframework.security.crypto.password.LdapShaPasswordEncoder());
    encoders.put("MD4", new org.springframework.security.crypto.password.Md4PasswordEncoder());
    encoders.put("MD5", new org.springframework.security.crypto.password.MessageDigestPasswordEncoder("MD5"));
    encoders.put("noop", org.springframework.security.crypto.password.NoOpPasswordEncoder.getInstance());
    encoders.put("pbkdf2", new Pbkdf2PasswordEncoder());
    encoders.put("scrypt", new SCryptPasswordEncoder());
    encoders.put("SHA-1", new org.springframework.security.crypto.password.MessageDigestPasswordEncoder("SHA-1"));
    encoders.put("SHA-256",
            new org.springframework.security.crypto.password.MessageDigestPasswordEncoder("SHA-256"));
    encoders.put("sha256", new org.springframework.security.crypto.password.StandardPasswordEncoder());
    encoders.put("argon2", new Argon2PasswordEncoder());
    return new DelegatingPasswordEncoder(encodingId, encoders);
}

现在关于 BCryptPasswordEncoder 类,它适用于一些默认值,例如:

  • 版本:$2a
  • 实力:10

如果声明以下内容会发生什么:

@Bean
PasswordEncoder bcryptEncoder() {
    return new BCryptPasswordEncoder(BCryptVersion.$2Y, 12);
}

如何添加或覆盖使用 custom BCryptPasswordEncoder 创建的 default BCryptPasswordEncoder 到默认设置中?我想保留所有其他默认值

注意:在PasswordEncoderFactories 类中(对于createDelegatingPasswordEncoder 方法),DelegatingPasswordEncoder 类在后台使用。该类也不提供覆盖的方法。

【问题讨论】:

    标签: spring-security


    【解决方案1】:

    如果您正在创建新应用程序,您很可能不需要DelegatingPasswordEncoder,而是应该使用自适应单向函数密码编码器,例如BCryptPasswordEncoder

    为此,您可以将 BCryptPasswordEncoder 公开为 bean。

    @Bean
    PasswordEncoder bcryptEncoder() {
        return new BCryptPasswordEncoder(BCryptVersion.$2Y, 12);
    }
    

    然后,当用户注册时,您可以使用BCryptPasswordEncoder 对其密码进行编码,然后再将其保存到您的数据存储中。例如:

    UserDetails userDetails = User
            .withUsername(username)
            .password(bcryptEncoder.encode(password))
            .roles("USER")
            .build();
    

    如果您要迁移现有应用程序,这就是DelegatingPasswordEncoder 的用武之地。

    DelegatingPasswordEncoder 允许验证多种格式的密码。它使用前缀 ID(例如 {bcrypt})来查找应该使用哪个 PasswordEncoder

    考虑一个使用明文密码的遗留应用程序。
    要迁移应用程序,您可以在明文密码前加上 {noop} 并使用 DelegatingPasswordEncoder,如下所示:

    @Bean
    public PasswordEncoder delegatingPasswordEncoder() {
        Map<String, PasswordEncoder> encoders = new HashMap<>();
        encoders.put("bcrypt", new BCryptPasswordEncoder(BCryptVersion.$2Y, 12));
        encoders.put("noop", NoOpPasswordEncoder.getInstance());
        return new DelegatingPasswordEncoder("bcrypt", encoders);
    }
    

    这个DelegatingPasswordEncoder 将使用BCryptPasswordEncoder 对任何新创建的密码进行编码和验证,同时仍然能够使用NoOpPasswordEncoder 验证旧的明文密码。

    Spring Security 提供 PasswordEncoderFactories.createDelegatingPasswordEncoder() 方法作为方便的默认值,但您的应用程序不太可能使用那么多不同的密码编码。
    更有可能的是,您的应用程序使用了 2 种不同的编码,一种是传统的(例如 noop),另一种是现代的(例如 bcrypt),在这种情况下,您可以使用类似于上述delegatingPasswordEncoderPasswordEncoder

    这些示例的意义在于,在大多数情况下,您不需要createDelegatingPasswordEncoder 中设置的默认值。但是,如果您仍想使用来自createDelegatingPasswordEncoder 的编码器,除了bcrypt,您可以使用如下所示的PasswordEncoder

    @Bean
    public PasswordEncoder delegatingPasswordEncoder() {
        Map<String, PasswordEncoder> encoders = new HashMap<>();
        // Use this encoder for bcrypt
        encoders.put("bcrypt", new BCryptPasswordEncoder(BCryptVersion.$2Y, 12));
        DelegatingPasswordEncoder delegatingPasswordEncoder =
                new DelegatingPasswordEncoder("bcrypt", encoders);
    
        PasswordEncoder defaultDelegatingPasswordEncoder =
                PasswordEncoderFactories.createDelegatingPasswordEncoder();
        // If a password ID does not match "bcrypt", use defaultDelegatingPasswordEncoder
        delegatingPasswordEncoder.setDefaultPasswordEncoderForMatches(defaultDelegatingPasswordEncoder);
    
        return delegatingPasswordEncoder;
    }
    

    【讨论】:

      猜你喜欢
      • 2019-07-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-05-09
      • 1970-01-01
      • 2015-03-20
      • 2016-05-03
      • 1970-01-01
      相关资源
      最近更新 更多