一、說明
當已上線的系統存在使用其他的加密方式加密的密碼資料,并且密碼 「不可逆」 時,而新的資料采用了其他的加密方式,則需要同時相容多種加密方式的密碼校驗。
例如下列幾種情況:
- 舊系統使用者的密碼采用了 「MD5」 的加密方式,而更新架構後的新系統則采用 「BCrypt」 的加密方式;
- 當割接曆史資料後會存在使用者表中密碼的 「加密方式不統一」 的問題,曆史資料為 「MD5」 新資料為 「BCrypt」;
- 是以需要系統支援同時相容多種加密方式的密碼校驗。
本文分享基于Security的PasswordEncoder來實作相容多套使用者密碼加密方式。
二、DelegatingPasswordEncoder
在 spring Security 5.0之後,預設的密碼加密方案其實是 DelegatingPasswordEncoder 它是一個代理類,而并非一種全新的密碼加密方案,可以用來代理多種不同的密碼加密方案。
「代碼參考」:
Map<String, PasswordEncoder> encoders = new HashMap<>();
encoders.put("bcrypt", new BCryptPasswordEncoder());
encoders.put("ldap", new org.springframework.security.crypto.password.LdapShaPasswordEncoder());
encoders.put("MD4", new org.springframework.security.crypto.password.Md4PasswordEncoder());
encoders.put("MD5", new org.springframework.security.crypto.password.MessageDigestPasswordEncoder("MD5"));
encoders.put("noop", org.springframework.security.crypto.password.NoOpPasswordEncoder.getInstance());
encoders.put("pbkdf2", new Pbkdf2PasswordEncoder());
encoders.put("scrypt", new SCryptPasswordEncoder());
encoders.put("SHA-1", new org.springframework.security.crypto.password.MessageDigestPasswordEncoder("SHA-1"));
encoders.put("SHA-256", new org.springframework.security.crypto.password.MessageDigestPasswordEncoder("SHA-256"));
encoders.put("sha256", new org.springframework.security.crypto.password.StandardPasswordEncoder());
encoders.put("argon2", new Argon2PasswordEncoder());
encoders.put("SM3", new SM3PasswordEncoder());
Assert.isTrue(encoders.containsKey(encodingId), encodingId + " is not found in idToPasswordEncoder");
DelegatingPasswordEncoder delegatingPasswordEncoder = new DelegatingPasswordEncoder(encodingId, encoders);
delegatingPasswordEncoder.setDefaultPasswordEncoderForMatches(encoders.get(encodingId));
return delegatingPasswordEncoder;
自動會根據資料的 encodingId 來使用對應的編譯器處理密碼
三、如何使用
3.1. 修改曆史密碼資料
修改舊的密碼資料的值,添加字首辨別 encodingId 格式如下:
- 無鹽值
{encodingId}密碼
例如源密碼為:$2a$10$EgTOU7PMe.3jaMwFsumdweJcnY3TsTqyuJEdSaSKxdgwYchAwUJ1C
則修改為:
{bcrypt}$2a$10$EgTOU7PMe.3jaMwFsumdweJcnY3TsTqyuJEdSaSKxdgwYchAwUJ1C
- 有鹽值
{encodingId}{salt}密碼
例如源密碼為:
0758f7131c6c95c8e3df05e1ac50214c
則修改為:
{MD5}{5Hstj}0758f7131c6c95c8e3df05e1ac50214c
encodingId 的值可參考 PwdEncoderUtil 類
如下圖所示:
3條記錄中,前兩條為原有的曆史記錄使用的是 MD5 的加密算法,然後新插入的資料使用的為 bcrypt 的加密算法,分别使用不同的字首辨別 encodingId
3.2. 配置 PasswordEncoder 對象
使用 DelegatingPasswordEncoder 類來定義 PasswordEncoder 并且指定預設加密方式為 bcrypt
@Bean
public PasswordEncoder passwordEncoder() {
return PwdEncoderUtil.getDelegatingPasswordEncoder("bcrypt");
}
以下兩種情況下都是使用預設的加密方式:
- 使用 encode 方法加密資料。
- 使用 matches 方法對比密文和原文時,密文沒有 encodingId 辨別。
3.3. 參考代碼
https://gitee.com/zlt2000/microservices-platform/blob/master/zlt-commons/zlt-common-core/src/main/java/com/central/common/utils/PwdEncoderUtil.java