如何移除Spring Security中已弃用的NoOpPasswordEncoder?
当然可以移除NoOpPasswordEncoder!它被弃用的核心原因是明文存储密码极度不安全,Spring Security早就推荐使用强哈希算法来处理密码存储。下面是具体的改造方案,一步一步来:
1. 替换为安全的密码编码器
我们用Spring Security官方推荐的BCryptPasswordEncoder,它基于BCrypt哈希算法,自带加盐机制,能有效抵御彩虹表攻击。
首先在配置类里定义一个PasswordEncoder的Bean,方便整个应用复用:
@Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); }
2. 修改内存认证配置
接下来调整configureGlobal方法,移除NoOpPasswordEncoder,改用我们定义的BCryptPasswordEncoder,同时要把明文密码换成加密后的哈希值(可以动态生成,也可以提前生成好)。
修改后的完整配置类:
@Configuration @EnableWebSecurity public class SpringSecurityConfig extends WebSecurityConfigurerAdapter { @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } @Override protected void configure(HttpSecurity http) throws Exception { http.csrf().disable() .authorizeRequests() // 注意规则顺序:先放行不需要认证的路径,再设置全局认证要求 .antMatchers("/").permitAll() .anyRequest().fullyAuthenticated() .and() .formLogin() .loginPage("/login").permitAll() .defaultSuccessUrl("/", true) .and() .logout().permitAll() .and() .httpBasic(); } @Autowired public void configureGlobal(AuthenticationManagerBuilder authBuilder, PasswordEncoder passwordEncoder) throws Exception { authBuilder.inMemoryAuthentication() .passwordEncoder(passwordEncoder) // 动态加密明文密码 .withUser("user") .password(passwordEncoder.encode("user")) .roles("USER") .and() .withUser("admin") .password(passwordEncoder.encode("admin")) .roles("USER", "ADMIN"); } }
小提示:调整规则顺序
注意我修改了authorizeRequests的顺序——Spring Security的规则是从上到下匹配,如果先写anyRequest().fullyAuthenticated(),再写antMatchers("/").permitAll(),那根路径/也会被要求认证,之前的代码顺序有逻辑问题,现在调整后才是正确的。
3. 测试代码无需修改
你的测试代码完全不用动:
@RunWith(SpringJUnit4ClassRunner.class) @AutoConfigureMockMvc @SpringBootTest public class SpringSecurityConfigTest { @Autowired MockMvc mockMvc; @Test public void userIsAuthenticatedTest() throws Exception { mockMvc.perform(formLogin().user("admin").password("admin")) .andExpect(authenticated()); } }
formLogin会自动使用我们配置的PasswordEncoder对传入的明文密码进行加密,再和内存中存储的哈希值比对,测试依然能正常通过。
可选优化:提前生成哈希值
如果你不想每次启动应用都重新加密密码,可以提前生成BCrypt哈希值(比如写个小代码片段生成),然后直接把哈希值写在代码里,比如:
.withUser("user") .password("$2a$10$EblZqNptyYvcLm/VwDCVAuBjzZOI7khzdyGPBr08PpIi0na624b8.") // 对应明文"user"的BCrypt哈希 .roles("USER")
这样改造后,你就彻底摆脱了已弃用的NoOpPasswordEncoder,同时功能完全正常,还大幅提升了密码存储的安全性!
内容的提问来源于stack exchange,提问作者Dmytro Manzhula

