New Password Storage In Spring Security 5 – Spring Security 5中的新密码存储

最后修改: 2018年 4月 6日

中文/混合/英文(键盘快捷键:t)

1. Introduction

1.介绍

With the latest Spring Security release, a lot has changed. One of those changes is how we can handle password encoding in our applications.

随着最新的Spring Security发布,很多东西都发生了变化。其中一个变化是我们如何在应用程序中处理密码编码。

In this tutorial, we’re going to explore some of these changes.

在本教程中,我们将探讨其中的一些变化。

Later, we’ll see how to configure the new delegation mechanism and how to update our existing password encoding, without our users recognizing it.

稍后,我们将看到如何配置新的委托机制以及如何更新我们现有的密码编码,而不被我们的用户识别。

2. Relevant Changes in Spring Security 5.x

2.Spring Security 5.x的相关变化

The Spring Security team declared the PasswordEncoder in org.springframework.security.authentication.encoding as deprecated. It was a logical move, as the old interface wasn’t designed for a randomly generated salt. Consequently, version 5 removed this interface.

Spring Security团队宣布org.springframework.security.authentication.encoding中的PasswordEncoder已经废弃。这是一个合乎逻辑的举动,因为旧的接口并不是为随机生成的盐而设计的。因此,版本5删除了这个接口。

Additionally, Spring Security changes the way it handles encoded passwords. In previous versions, each application employed one password encoding algorithm only.

此外,Spring Security改变了它处理编码密码的方式。在以前的版本中,每个应用程序只采用一种密码编码算法。

By default, StandardPasswordEncoder dealt with that. It used SHA-256 for the encoding. By changing the password encoder, we could switch to another algorithm. But our application had to stick to exactly one algorithm.

默认情况下,StandardPasswordEncoder采用了这种方式。它使用SHA-256进行编码。通过改变密码编码器,我们可以切换到另一种算法。但我们的应用程序必须完全坚持一种算法。

Version 5.0 introduces the concept of password encoding delegation. Now, we can use different encodings for different passwords. Spring recognizes the algorithm by an identifier prefixing the encoded password.

5.0版本引入了密码编码委托的概念。现在,我们可以为不同的密码使用不同的编码。Spring通过编码的密码前缀的标识符来识别算法。

Here’s an example of a bcrypt encoded password:

下面是一个bcrypt编码密码的例子。

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

Note how bcrypt is specified in curly braces in the very beginning.

注意bcrypt是如何在一开始就用大括号指定的。

3. Delegation Configuration

3.授权配置

If the password hash has no prefix, the delegation process uses a default encoder. Hence, by default, we get the StandardPasswordEncoder.

如果密码哈希值没有前缀,委托过程就会使用默认的编码器。因此,默认情况下,我们会得到StandardPasswordEncoder。

That makes it compatible with the default configuration of previous Spring Security versions.

这使得它与以前的Spring Security版本的默认配置兼容。

With version 5, Spring Security introduces PasswordEncoderFactories.createDelegatingPasswordEncoder(). This factory method returns a configured instance of DelegationPasswordEncoder.

在版本5中,Spring Security引入了PasswordEncoderFactories.createDelegatingPasswordEncoder().该工厂方法返回DelegationPasswordEncoder的一个配置实例。

For passwords without a prefix, that instance ensures the just mentioned default behavior. And for password hashes that contain a prefix, the delegation is done accordingly.

对于没有前缀的密码,该实例确保刚才提到的默认行为。而对于包含前缀的密码哈希值,则进行相应的委托处理。

The Spring Security team lists the supported algorithms in the latest version of the corresponding JavaDoc.

Spring 安全团队在最新版本的相应的 JavaDoc中列出了支持的算法。

Of course, Spring lets us configure this behavior.

当然,Spring允许我们配置这种行为。

Let’s assume we want to support:

让我们假设我们想支持。

  • bcrypt as our new default
  • scrypt as an alternative
  • SHA-256 as the currently used algorithm.

The configuration for this set-up will look like this:

这种设置的配置将看起来像这样。

@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. Migrating the Password Encoding Algorithm

4.迁移密码编码算法

In the previous section, we explored how to configure password encoding according to our needs. Therefore, now we’ll work on how to switch an already encoded password to a new algorithm.

在上一节中,我们探讨了如何根据我们的需求来配置密码编码。因此,现在我们将研究如何将已经编码的密码转换为新的算法。

Let’s imagine we want to change the encoding from SHA-256 to bcrypt, however, we don’t want our user to change their passwords.

假设我们想把编码从SHA-256改为bcrypt,但是,我们不希望用户改变他们的密码。

One possible solution is to use the login request. At this point, we can access the credentials in plain text. That is the moment we can take the current password and re-encode it.

一个可能的解决方案是使用登录请求。在这个时候,我们可以访问纯文本的凭证。这时我们可以将当前的密码进行重新编码。

Consequently, we can use Spring’s AuthenticationSuccessEvent for that. This event fires after a user successfully logged into our application.

因此,我们可以使用Spring的AuthenticationSuccessEvent来实现。该事件在用户成功登录我们的应用程序后发生。

Here is the example code:

以下是示例代码。

@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();
        }
    };
}

In the previous snippet:

在之前的片段中。

  • We retrieved the user password in clear text from the provided authentication details
  • Created a new password hash with the new algorithm
  • Removed the clear text password from the authentication token

By default, extracting the password in clear text wouldn’t be possible because Spring Security deletes it as soon as possible.

默认情况下,提取明文密码是不可能的,因为Spring Security会尽快将其删除。

Hence, we need to configure Spring so that it keeps the cleartext version of the password.

因此,我们需要配置Spring,使其保留密码的明文版本。

Additionally, we need to register our encoding delegation:

此外,我们需要注册我们的编码委托。

@Configuration
public class PasswordStorageWebSecurityConfigurer {

    @Bean
    public AuthenticationManager authManager(HttpSecurity http) throws Exception {
        AuthenticationManagerBuilder authenticationManagerBuilder = 
            http.getSharedObject(AuthenticationManagerBuilder.class);
        authenticationManagerBuilder.eraseCredentials(false)
            .userDetailsService(getUserDefaultDetailsService())
            .passwordEncoder(passwordEncoder());
        return authenticationManagerBuilder.build();
    }

   // ...
}

5. Conclusion

5.结论

In this quick article, we talked about some new password encoding features available in 5.x.

在这篇快速文章中,我们谈到了5.x中的一些新的密码编码功能。

We also saw how to configure multiple password encoding algorithms to encode our passwords. Furthermore, we explored a way to change the password encoding, without breaking the existing one.

我们还看到了如何配置多种密码编码算法来对我们的密码进行编码。此外,我们探索了一种改变密码编码的方法,而不破坏现有的密码编码。

Lastly, we described how to use Spring events to update encrypted user password transparently, allowing us to seamlessly change our encoding strategy without disclosing that to our users.

最后,我们描述了如何使用Spring事件来透明地更新加密的用户密码,使我们能够无缝地改变我们的编码策略而不向用户透露。

Lastly and as always, all code examples are available in our GitHub repository.

最后,像往常一样,所有的代码实例都可以在我们的GitHub仓库中找到。