我知道在 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 类在幕后使用。该类也不提供重写方法。
新应用程序,您很可能不需要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),在这种情况下,您可以使用类似于上述
PasswordEncoder
的
delegatingPasswordEncoder
。这些示例的要点是,在大多数情况下,您不需要
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;
}