#spring-security
#spring-безопасность
Вопрос:
Я знаю, что в 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"
И решение заключается в определении a 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);
}
Как я могу добавить или переопределить значение по умолчанию BCryptPasswordEncoder
, созданное с помощью пользовательского BCryptPasswordEncoder
, в настройки по умолчанию? Я хочу сохранить все остальные значения по умолчанию
Примечание: в классе PasswordEncoderFactories (для createDelegatingPasswordEncoder
метода) класс DelegatingPasswordEncoder используется за кулисами. Этот класс также не предлагает подход к переопределению.
Ответ №1:
Если вы создаете новое приложение, вам, скорее всего, не понадобится DelegatingPasswordEncoder
и вместо этого следует использовать, например, адаптивный кодировщик паролей с односторонней функцией BCryptPasswordEncoder
.
Для этого вы можете представить a BCryptPasswordEncoder
как компонент.
@Bean
PasswordEncoder bcryptEncoder() {
return new BCryptPasswordEncoder(BCryptVersion.$2Y, 12);
}
Затем, когда пользователь регистрируется, вы можете закодировать его пароль с помощью BCryptPasswordEncoder
, прежде чем сохранить его в хранилище данных. Например:
UserDetails userDetails = User
.withUsername(username)
.password(bcryptEncoder.encode(password))
.roles("USER")
.build();
Если вы переносите существующее приложение, это DelegatingPasswordEncoder
полезно.
DelegatingPasswordEncoder
Позволяет проверять пароли в нескольких форматах. Он использует идентификатор с префиксом (например {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;
}