Spring Security 5中的默认密码编码器

2023/05/17

1. 概述

在Spring Security 4中,可以使用基于内存的身份验证以纯文本形式存储密码。

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

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

2. Spring Security 4

首先,下面是一个标准的Security配置,该配置提供简单的内存验证(对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();
    }
}

此配置定义为所有/private/请求需要身份验证,以及/public/下的所有内容可以公共访问。

如果我们在Spring Security 5下使用相同的配置,当我们启动应用发送请求时,将出现以下错误:

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

该错误告诉我们,由于没有为我们的内存身份验证配置密码编码器,因此无法解码给定的密码

3. Spring Security 5

我们可以通过使用PasswordEncoderFactories类定义DelegatingPasswordEncoder来解决此错误。

我们使用此编码器通过AuthenticationManagerBuilder配置我们的用户:

@Configuration
public class InMemoryAuthWebSecurityConfigurer extends WebSecurityConfigurerAdapter {

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

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

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

尽管我们可以定义自己的一组密码编码器,但建议坚持使用PasswordEncoderFactories中提供的默认编码器。自Spring Security 5.7.0-M2版本以来,Spring不赞成使用WebSecurityConfigureAdapter,并建议在没有WebSecurityConfigure Adapter的情况下创建配置,本文将对此进行了更详细的解释。

4. NoOpPasswordEncoder

如果出于任何原因,我们不想对配置的密码进行编码,我们可以使用NoOpPasswordEncoder。为此,我们只需在提供给password()方法的密码字符串前加上{noop}前缀标识符:

@Configuration
public class InMemoryNoOpAuthWebSecurityConfigurer extends WebSecurityConfigurerAdapter {

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

这样,当Spring Security将用户提供的密码与我们上面配置的密码进行比较时,Spring Security将在背后使用NoOpPasswordEncoder。

但是请注意,我们绝不应该在生产环境下使用这种方法!正如官方文档所说,NoOpPasswordEncoder已被弃用,表明它是一个遗留实现,并且使用它被认为是不安全的

5. 更换现有密码编码

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

  • 使用编码的值更新纯文本存储的密码:
String encoded = new BCryptPasswordEncoder().encode(plainTextPassword);
  • 使用已知的编码器标识符为哈希存储密码添加前缀:
{bcrypt}$2a$10$MF7hYnWLeLT66gNccBgxaONZHbrSMjlUofkp50sSpBw2PJjUqU.zS
{sha256}97cde38028ad898ebc02e690819fa220e88c62e0699403e94fff291cfffaf8410849f27605abcbc0
  • 当存储密码的编码机制未知时,请求用户更新他们的密码。

6. 总结

在这个教程中,我们介绍了使用新的密码存储机制将有效的Spring 4内存验证配置更新为Spring 5。

与往常一样,本教程的完整源代码可在GitHub上获得。

Show Disqus Comments

Post Directory

扫码关注公众号:Taketoday
发送 290992
即可立即永久解锁本站全部文章