如何覆盖通过PasswordEncoderFactories创建的默认BCryptPasswordEncoder?

Man*_*dan 3 spring-security bcrypt

我知道在 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"
Run Code Online (Sandbox Code Playgroud)

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

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"
Run Code Online (Sandbox Code Playgroud)

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

@Bean
PasswordEncoder encoder() {
    return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}
Run Code Online (Sandbox Code Playgroud)

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

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

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

@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);
}
Run Code Online (Sandbox Code Playgroud)

如何将自定义创建的默认值添加或覆盖默认 BCryptPasswordEncoder设置中?我想保留所有其他默认值 BCryptPasswordEncoder

注意:在PasswordEncoderFactories类(对于该createDelegatingPasswordEncoder方法)中,DelegatingPasswordEncoder类在幕后使用。该类也不提供重写方法。

Ele*_*ana 7

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

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

@Bean
PasswordEncoder bcryptEncoder() {
    return new BCryptPasswordEncoder(BCryptVersion.$2Y, 12);
}
Run Code Online (Sandbox Code Playgroud)

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

UserDetails userDetails = User
        .withUsername(username)
        .password(bcryptEncoder.encode(password))
        .roles("USER")
        .build();
Run Code Online (Sandbox Code Playgroud)

如果您要迁移现有应用程序,这就是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);
}
Run Code Online (Sandbox Code Playgroud)

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

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

这些示例的要点是说,在大多数情况下,您不需要 中设置的默认值createDelegatingPasswordEncoder。但是,如果您仍然想使用 中的编码器(除了createDelegatingPasswordEncoderbcrypt,您可以使用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;
}
Run Code Online (Sandbox Code Playgroud)