1. 概述
在 Spring Security 4 中,可以使用內存身份驗證方式存儲密碼,即明文存儲密碼。
Spring Security 5 中的密碼管理流程進行了重大改進,引入了更安全的默認機制,用於編碼和解碼密碼。這意味着,如果您的 Spring 應用存儲密碼為明文,升級到 Spring Security 5 可能會導致問題。
在本簡短教程中,我們將描述這些潛在問題,並演示解決方案。
2. Spring Security 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();
}
}
此配置定義了所有對 /private/ 映射方法以及 /public/ 下一切內容的身份驗證。
如果在 Spring Security 5 中使用相同的配置,將會出現以下錯誤:
java.lang.IllegalArgumentException: There is no PasswordEncoder mapped for the id "null"錯誤表明提供的密碼由於未配置內存身份驗證的密碼編碼器,因此無法解碼。
3. Spring Security 5
我們可以通過定義一個 DelegatingPasswordEncoder 與 PasswordEncoderFactories 類一起,來解決此問題。
我們使用此編碼器來配置我們的用户:
@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);
}
}現在,使用這種配置,我們使用BCrypt將密碼存儲在以下格式中:
{bcrypt}$2a$10$MF7hYnWLeLT66gNccBgxaONZHbrSMjlUofkp50sSpBw2PJjUqU.zS
雖然我們可以定義自己的密碼編碼器集,但建議使用 默認編碼器,這些編碼器位於 PasswordEncoderFactories 中。
自 Spring Security 5.7.0-M2 版本起,Spring 已棄用 WebSecurityConfigureAdapter 並建議在不使用它的情況下創建配置。 這篇文章 會更詳細地解釋這一點。
3.2. NoOpPasswordEncoder
如果出於某種原因,我們不想對配置的密碼進行編碼,我們可以利用 NoOpPasswordEncoder。
要做到這一點,我們只需將提供的密碼短語前綴到 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);
}
}這樣一來,Spring Security 將在比較用户提供的密碼與我們配置的密碼時,在底層使用 NoOpPasswordEncoder。
不過,請注意 我們絕不應該在生產應用程序中使用這種方法!正如官方文檔所説,NoOpPasswordEncoder 已經被棄用,這表示它是一個遺留實現,使用它被認為是不安全的。
3.3. 現有密碼遷移
我們可以通過以下步驟將現有密碼更新為推薦的 Spring Security 5 標準:
- 使用編碼方式更新存儲為純文本的密碼值。
String encoded = new BCryptPasswordEncoder().encode(plainTextPassword);
- 使用已知編碼器標識符對哈希存儲的密碼進行前綴:
{bcrypt}$2a$10$MF7hYnWLeLT66gNccBgxaONZHbrSMjlUofkp50sSpBw2PJjUqU.zS
{sha256}97cde38028ad898ebc02e690819fa220e88c62e0699403e94fff291cfffaf8410849f27605abcbc0
<ul>
<li>要求用户在不知道存儲密碼的編碼機制時更新密碼</li>
</li >
4. 結論
在本快速示例中,我們使用新的密碼存儲機制,將有效的 Spring 4 內嵌式身份驗證配置更新為 Spring 5。