Перейти к основному содержимому

Новое хранилище паролей в Spring Security 5

· 4 мин. чтения

1. Введение

С последним выпуском Spring Security многое изменилось. Одним из таких изменений является то, как мы можем обрабатывать кодировку паролей в наших приложениях.

В этом уроке мы рассмотрим некоторые из этих изменений.

Позже мы увидим, как настроить новый механизм делегирования и как обновить нашу существующую кодировку пароля так, чтобы наши пользователи не узнали ее.

2. Соответствующие изменения в Spring Security 5.x

Команда Spring Security объявила PasswordEncoder в org.springframework.security.authentication.encoding устаревшим. Это был логичный ход, так как старый интерфейс не был рассчитан на случайно сгенерированную соль. Следовательно, версия 5 удалила этот интерфейс.

Кроме того, Spring Security изменяет способ обработки закодированных паролей. В предыдущих версиях каждое приложение использовало только один алгоритм шифрования пароля.

По умолчанию этим занимается StandardPasswordEncoder . Он использовал SHA-256 для кодирования. Сменив кодировщик пароля, мы могли бы переключиться на другой алгоритм. Но наше приложение должно было придерживаться ровно одного алгоритма.

В версии 5.0 представлена концепция делегирования кодировки паролей. Теперь мы можем использовать разные кодировки для разных паролей. Spring распознает алгоритм по идентификатору, предшествующему закодированному паролю.

Вот пример пароля, закодированного bcrypt:

{bcrypt}$2b$12$FaLabMRystU4MLAasNOKb.HUElBAabuQdX59RWHq5X.9Ghm692NEi

Обратите внимание, что bcrypt указан в фигурных скобках в самом начале.

3. Конфигурация делегирования

Если хэш пароля не имеет префикса, процесс делегирования использует кодировщик по умолчанию. Следовательно, по умолчанию мы получаем StandardPasswordEncoder.

Это делает его совместимым с конфигурацией по умолчанию предыдущих версий Spring Security.

В версии 5 Spring Security представляет PasswordEncoderFactories.createDelegatingPasswordEncoder(). Этот фабричный метод возвращает сконфигурированный экземпляр DelegationPasswordEncoder .

Для паролей без префикса этот экземпляр обеспечивает только что упомянутое поведение по умолчанию. А для хэшей паролей, содержащих префикс, делегирование выполняется соответствующим образом.

Команда Spring Security перечисляет поддерживаемые алгоритмы в последней версии соответствующего JavaDoc .

Конечно, Spring позволяет нам настроить это поведение.

Предположим, мы хотим поддерживать:

  • bcrypt в качестве нашего нового значения по умолчанию
  • скрипт как альтернатива
  • SHA-256 как используемый в настоящее время алгоритм.

Конфигурация для этой установки будет выглядеть так:

@Bean
public PasswordEncoder delegatingPasswordEncoder() {
PasswordEncoder defaultEncoder = new StandardPasswordEncoder();
Map<String, PasswordEncoder> encoders = new HashMap<>();
encoders.put("bcrypt", new BCryptPasswordEncoder());
encoders.put("scrypt", new SCryptPasswordEncoder());

DelegatingPasswordEncoder passworEncoder = new DelegatingPasswordEncoder(
"bcrypt", encoders);
passworEncoder.setDefaultPasswordEncoderForMatches(defaultEncoder);

return passworEncoder;
}

4. Миграция алгоритма кодирования паролей

В предыдущем разделе мы рассмотрели, как настроить кодировку пароля в соответствии с нашими потребностями. Поэтому сейчас поработаем над тем, как переключить уже зашифрованный пароль на новый алгоритм.

Давайте представим, что мы хотим изменить кодировку с SHA-256 на bcrypt , однако мы не хотим, чтобы наш пользователь менял свои пароли.

Одним из возможных решений является использование запроса на вход. На этом этапе мы можем получить доступ к учетным данным в виде обычного текста. В этот момент мы можем взять текущий пароль и перекодировать его.

Следовательно, для этого мы можем использовать Spring AuthenticationSuccessEvent . Это событие срабатывает после того, как пользователь успешно вошел в наше приложение.

Вот пример кода:

@Bean
public ApplicationListener<AuthenticationSuccessEvent>
authenticationSuccessListener( PasswordEncoder encoder) {
return (AuthenticationSuccessEvent event) -> {
Authentication auth = event.getAuthentication();

if (auth instanceof UsernamePasswordAuthenticationToken
&& auth.getCredentials() != null) {

CharSequence clearTextPass = (CharSequence) auth.getCredentials();
String newPasswordHash = encoder.encode(clearTextPass);

// [...] Update user's password

((UsernamePasswordAuthenticationToken) auth).eraseCredentials();
}
};
}

В предыдущем фрагменте:

  • Мы получили пароль пользователя в виде открытого текста из предоставленных данных аутентификации.
  • Создан новый хэш пароля с новым алгоритмом
  • Удален открытый текстовый пароль из токена аутентификации.

По умолчанию извлечение пароля в виде открытого текста невозможно, поскольку Spring Security удаляет его как можно скорее.

Следовательно, нам нужно настроить Spring так, чтобы он сохранял версию пароля в открытом виде.

Кроме того, нам нужно зарегистрировать нашу делегацию кодирования:

@Configuration
public class PasswordStorageWebSecurityConfigurer
extends WebSecurityConfigurerAdapter {

@Override
protected void configure(AuthenticationManagerBuilder auth)
throws Exception {
auth.eraseCredentials(false)
.passwordEncoder(delegatingPasswordEncoder());
}

// ...
}

5. Вывод

В этой быстрой статье мы рассказали о некоторых новых функциях кодирования паролей, доступных в версии 5.x.

Мы также увидели, как настроить несколько алгоритмов кодирования паролей для кодирования наших паролей. Кроме того, мы изучили способ изменить кодировку пароля, не нарушая существующую.

Наконец, мы описали, как использовать события Spring для прозрачного обновления зашифрованного пароля пользователя, что позволяет нам беспрепятственно изменять нашу стратегию кодирования, не раскрывая это нашим пользователям.

Наконец, как всегда, все примеры кода доступны в нашем репозитории GitHub .