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

Хранение нового пароля в Spring Security 5

1. Вступление

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

В этом руководстве мы собираемся изучить некоторые из этих изменений.

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

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

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

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

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

Version 5.0 introduces the concept of password encoding delegation. Теперь мы можем использовать разные кодировки для разных паролей. Spring распознает алгоритм по идентификатору с префиксом закодированного пароля.

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

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

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

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

If the password hash has no prefix, the delegation process uses a default encoder. Следовательно, по умолчанию мы получаемStandardPasswordEncoder.

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

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

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

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

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

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

  • bcrypt по умолчанию

  • scrypt в качестве альтернативы

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

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

@Bean
public PasswordEncoder delegatingPasswordEncoder() {
    PasswordEncoder defaultEncoder = new StandardPasswordEncoder();
    Map 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. Перенос алгоритма кодирования паролей

В предыдущем разделе мы рассмотрели, как настроить кодирование пароля в соответствии с нашими потребностями. Therefore, now we’ll work on how to switch an already encoded password to a new algorithm.с

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

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

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

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

@Bean
public ApplicationListener
  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 repository.