1. 概述
在 Spring Security 4 中,可以使用內存身份驗證將密碼存儲為明文。
版本 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
- 要求用户在不知道編碼機制時更新其密碼
4. 結論
在本快速示例中,我們使用新的密碼存儲機制,將有效的 Spring 4 內嵌式身份驗證配置更新為 Spring 5。