Default Password Encoder in Spring Security 5 – Spring Security 5中的默认密码编码器

最后修改: 2018年 8月 10日

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

1. Overview

1.概述

In Spring Security 4, it was possible to store passwords in plain text using in-memory authentication.

在Spring Security 4中,可以使用内存认证来存储纯文本的密码。

A major overhaul of the password management process in version 5 has introduced a more secure default mechanism for encoding and decoding passwords. This means that if your Spring application stores passwords in plain text, upgrading to Spring Security 5 may cause problems.

第5版中对密码管理过程进行了大修,引入了更安全的密码编码和解码的默认机制。这意味着,如果你的Spring应用程序以纯文本形式存储密码,升级到Spring Security 5可能会导致问题。

In this short tutorial, we’ll describe one of those potential problems and demonstrate a solution.

在这个简短的教程中,我们将描述其中一个潜在的问题并演示一个解决方案。

2. Spring Security 4

2.Spring安全 4

We’ll start by showing a standard security configuration that provides simple in-memory authentication (valid for Spring 4):

我们将首先展示一个标准的安全配置,提供简单的内存认证(对Spring 4有效)。

@Configuration
public class InMemoryAuthWebSecurityConfigurer 
  extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(AuthenticationManagerBuilder auth) 
      throws Exception {
        auth.inMemoryAuthentication()
          .withUser("spring")
          .password("secret")
          .roles("USER");
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
          .antMatchers("/private/**")
          .authenticated()
          .antMatchers("/public/**")
          .permitAll()
          .and()
          .httpBasic();
    }
}

This configuration defines authentication for all /private/ mapped methods and public access for everything under /public/.

此配置为所有/private/映射的方法定义了认证,为/public/.下的所有内容定义了公共访问。

If we use the same configuration under Spring Security 5, we’ll get the following error:

如果我们在Spring Security 5下使用同样的配置,我们会得到以下错误。

java.lang.IllegalArgumentException: There is no PasswordEncoder mapped for the id "null"

The error tells us that the given password couldn’t be decoded since no password encoder was configured for our in-memory authentication.

这个错误告诉我们,给定的密码不能被解码,因为没有为我们的内存认证配置密码编码器

3. Spring Security 5

3.Spring安全 5

We can fix this error by defining a DelegatingPasswordEncoder with the PasswordEncoderFactories class.

我们可以通过定义一个DelegatingPasswordEncoderPasswordEncoderFactories类来解决这个错误。

We use this encoder to configure our user :

我们用这个编码器来配置我们的用户。

@Configuration
public class InMemoryAuthWebSecurityConfigurer {

    @Bean
    public InMemoryUserDetailsManager userDetailsService() {
        PasswordEncoder encoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();
        UserDetails user = User.withUsername("spring")
            .password(encoder.encode("secret"))
            .roles("USER")
            .build();
        return new InMemoryUserDetailsManager(user);
    }
}

Now, with this configuration, we’re storing our in-memory password using BCrypt in the following format:

现在,通过这种配置,我们使用BCrypt以如下格式存储我们的内存密码。

{bcrypt}$2a$10$MF7hYnWLeLT66gNccBgxaONZHbrSMjlUofkp50sSpBw2PJjUqU.zS

Although we can define our own set of password encoders, it’s recommended to stick with the default encoders provided in PasswordEncoderFactories.

虽然我们可以定义自己的密码编码器集,但建议坚持使用PasswordEncoderFactories中提供的默认的编码器

Since Spring Security version 5.7.0-M2, Spring deprecates the use of WebSecurityConfigureAdapter and suggests creating configurations without it. This article explains it in more detail.

自 Spring Security 5.7.0-M2 版起,Spring 废除了WebSecurityConfigureAdapter 的使用,并建议在没有它的情况下创建配置。这篇文章更详细地解释了这一点。

3.2. NoOpPasswordEncoder

3.2.NoOpPasswordEncoder

If, for any reason, we don’t want to encode the configured password, we can make use of the NoOpPasswordEncoder.

如果出于任何原因,我们不想对配置的密码进行编码,我们可以利用NoOpPasswordEncoder

To do so, we simply prefix the passphrase we provide to the password() method with the {noop} identifier:

为此,我们只需在提供给password()方法的口令前加上{noop}标识。

@Configuration
public class InMemoryNoOpAuthWebSecurityConfigurer {

    @Bean
    public InMemoryUserDetailsManager userDetailsService() {
        UserDetails user = User.withUsername("spring")
            .password("{noop}secret")
            .roles("USER")
            .build();
        return new InMemoryUserDetailsManager(user);
    }
}

This way, Spring Security will use the NoOpPasswordEncoder under the hood when it compares the password provided by the user with the one we configured above.

这样,Spring Security在比较用户提供的密码和我们上面配置的密码时,将使用引擎盖下的NoOpPasswordEncoder

Note, however, that we should never use this approach on the production application! As the official documentation says, the NoOpPasswordEncoder has been deprecated to indicate that it’s a legacy implementation, and using it is considered insecure.

然而,请注意,我们绝不应该在生产应用中使用这种方法!正如官方文档所说,NoOpPasswordEncoder已经被废弃表明它是一个传统的实现,并且使用它被认为是不安全的

3.3. Migrating Existing Passwords

3.3.迁移现有的密码

We can update existing passwords to the recommended Spring Security 5 standards by:

我们可以通过以下方式将现有密码更新为推荐的Spring Security 5标准。

  • Updating plain text stored passwords with their value encoded:
String encoded = new BCryptPasswordEncoder().encode(plainTextPassword);
  • Prefixing hashed stored passwords with their known encoder identifier:
{bcrypt}$2a$10$MF7hYnWLeLT66gNccBgxaONZHbrSMjlUofkp50sSpBw2PJjUqU.zS
{sha256}97cde38028ad898ebc02e690819fa220e88c62e0699403e94fff291cfffaf8410849f27605abcbc0
  • Requesting users to update their passwords when the encoding-mechanism for stored passwords is unknown

4. Conclusion

4.总结

In this quick example, we updated a valid Spring 4 in-memory authentication configuration to Spring 5 using the new password storage mechanism.

在这个快速的例子中,我们使用新的密码存储机制将一个有效的Spring 4内存认证配置更新到Spring 5。

As always, you can find the source code over on the GitHub project.

一如既往,你可以在GitHub项目上找到源代码。