1. 簡介
隨着最新版 Spring Security 的發佈,許多方面都發生了變化。其中之一是如何在我們的應用程序中處理密碼編碼。
在本教程中,我們將探索這些變化。
稍後,我們將瞭解如何配置新的委託機制,以及如何更新我們現有的密碼編碼,而不會讓我們的用户察覺到變化。
2. Spring Security 5.x 中的相關變更
Spring Security 團隊宣佈將 PasswordEncoder 在 org.springframework.security.authentication.encoding 中標記為棄用。 這是一項合理的舉措,因為舊接口並未設計用於隨機生成的鹽。 因此,版本 5 移除了此接口。
此外,Spring Security 改變了它處理編碼密碼的方式。 在以前的版本中,每個應用程序僅使用一種密碼編碼算法。
默認情況下,StandardPasswordEncoder 處理了此任務。 它使用 SHA-256 進行編碼。 通過更改密碼編碼器,我們可以切換到另一種算法。 但是,我們的應用程序必須堅持使用一種算法。
版本 5.0 引入了密碼編碼委託的概念。 現在,我們可以為不同的密碼使用不同的編碼。 Spring 通過為編碼密碼添加的前綴來識別算法。
以下是 bcrypt 編碼密碼的示例:
{bcrypt}$2b$12$FaLabMRystU4MLAasNOKb.HUElBAabuQdX59RWHq5X.9Ghm692NEi請注意,bcrypt 的指定方式在開頭使用花括號。
3. 委託配置
如果密碼哈希沒有前綴,委託過程將使用默認編碼器。因此,默認情況下,我們獲取 StandardPasswordEncoder。
這使其與舊版 Spring Security 的默認配置兼容。
在 Spring Security 5 中,Spring Security 引入了 PasswordEncoderFactories.createDelegatingPasswordEncoder()。此工廠方法返回一個配置的 DelegationPasswordEncoder 實例。
對於沒有前綴的密碼,該實例確保上述默認行為。對於包含前綴的密碼哈希,委託將相應地進行。
Spring Security 團隊在最新版本的 相關 JavaDoc 中列出了支持的算法。
當然,Spring 允許我們配置此行為。
假設我們想要支持:
- bcrypt 作為我們的新默認值
- scrypt 作為替代方案
- SHA-256 作為當前使用的算法。
此配置的設置將如下所示:
@Bean
public PasswordEncoder delegatingPasswordEncoder() {
PasswordEncoder defaultEncoder = new StandardPasswordEncoder();
Map<String, PasswordEncoder> encoders = new HashMap<>();
encoders.put("bcrypt", new BCryptPasswordEncoder());
encoders.put("scrypt", new SCryptPasswordEncoder(1, 1, 1, 1, 10));
DelegatingPasswordEncoder passworEncoder = new DelegatingPasswordEncoder(
"bcrypt", encoders);
passworEncoder.setDefaultPasswordEncoderForMatches(defaultEncoder);
return passworEncoder;
}4. 遷移密碼編碼算法
在上一節中,我們探討了如何根據我們的需求配置密碼編碼。 因此,現在我們將處理如何將已編碼的密碼切換到新的算法。
假設我們想要將編碼方式從 SHA-256 更改為 bcrypt,但我們不想讓用户更改他們的密碼。
一個可能的解決方案是使用登錄請求。 在此時,我們可以訪問平時的憑據。 這就是我們能夠獲取當前密碼並重新編碼它的時刻。
因此,我們可以使用 Spring 的 AuthenticationSuccessEvent 來完成此操作。 此事件在用户成功登錄我們的應用程序後觸發。
以下是示例代碼:
@Bean
public ApplicationListener<AuthenticationSuccessEvent>
authenticationSuccessListener( PasswordEncoder encoder) {
return (AuthenticationSuccessEvent event) -> {
Authentication auth = event.getAuthentication();
if (auth instanceof UsernamePasswordAuthenticationToken
&& auth.getCredentials() != null) {
CharSequence clearTextPass = (CharSequence) auth.getCredentials();
String newPasswordHash = encoder.encode(clearTextPass);
// [...] Update user's password
((UsernamePasswordAuthenticationToken) auth).eraseCredentials();
}
};
}在之前的片段中:
- 我們從提供的身份驗證詳情中檢索了明文用户密碼。
- 創建了一個新的密碼哈希,使用了新的算法。
- 從身份驗證令牌中刪除了明文密碼。
默認情況下,由於 Spring Security 會盡快刪除明文密碼,因此無法提取密碼。
因此,我們需要配置 Spring,以便保留明文密碼版本。
此外,我們需要註冊我們的編碼委託。
@Configuration
public class PasswordStorageWebSecurityConfigurer {
@Bean
public AuthenticationManager authManager(HttpSecurity http) throws Exception {
AuthenticationManagerBuilder authenticationManagerBuilder =
http.getSharedObject(AuthenticationManagerBuilder.class);
authenticationManagerBuilder.eraseCredentials(false)
.userDetailsService(getUserDefaultDetailsService())
.passwordEncoder(passwordEncoder());
return authenticationManagerBuilder.build();
}
// ...
}5. 結論
在本文中,我們介紹了 5.x 版本中可用的新密碼編碼功能。
我們還了解了如何配置多個密碼編碼算法來編碼密碼。此外,我們還探索了一種更改密碼編碼的方法,而無需破壞現有編碼。
最後,我們描述瞭如何使用 Spring 事件以透明的方式更新加密用户密碼,從而使我們能夠在不向用户披露的情況下無縫更改編碼策略。