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

q3qa4bjr  于 2023-06-23  发布在  Spring
关注(0)|答案(1)|浏览(108)

我知道在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);
}

如何将使用 customBCryptPasswordEncoder创建的 defaultBCryptPasswordEncoder添加或覆盖到默认设置?我想保留所有其他默认值

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

zour9fqk

zour9fqk1#

如果您正在创建一个新应用,您很可能不需要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;
}

相关问题