1 BCryptPasswordEncoder密碼加密的思考

1,密碼存儲 僅僅依賴于普通的 hash 算法(如 md5,sha256)是不合適的,他主要有 3 個特點(diǎn):

  1. 同一密碼生成的 hash 值一定相同
  2. 不同密碼的生成的 hash 值可能相同(md5 的碰撞問題相比 sha256 還要嚴(yán)重)
  3. 計(jì)算速度快。

\color{red}{導(dǎo)致的問題:以上三點(diǎn)結(jié)合在一起,破解此類算法成了不是那么困難的一件事}

2, BCryptPasswordEncoder的優(yōu)勢

  1. 每次編碼都會隨機(jī)生成不一樣的salt值去編碼,所以相同密碼加密后值不同
  2. 可控制代價(jià)因子(也就是循環(huán)加密次數(shù)的控制)讓加密速度慢,破解代價(jià)增大。

3, BCryptPasswordEncoder后期的擴(kuò)展問題

@Bean
PasswordEncoder passwordEncoder(){
    return new BCryptPasswordEncoder();
}

問題:

  1. spring security 怎么這么坑,原來的密碼編碼器都給改了,我需要怎么遷移舊密碼編碼的應(yīng)用程序?
  2. 萬一以后出了更高效的加密算法,這種笨重的硬編碼方式配置密碼編碼器是不是不夠靈活?

方案:
在 spring security 5 提供了這樣一個思路,應(yīng)該將密碼編碼之后的 hash 值和加密方式一起存儲,并提供了一個 DelegatingPasswordEncoder 來作為眾多密碼密碼編碼方式的集合。

@Bean
PasswordEncoder passwordEncoder(){
    return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}

public class PasswordEncoderFactories {
   public static PasswordEncoder createDelegatingPasswordEncoder() {
      String encodingId = "bcrypt";
      Map<String, PasswordEncoder> encoders = new HashMap<>();
      encoders.put(encodingId, new BCryptPasswordEncoder());
      encoders.put("ldap", new LdapShaPasswordEncoder());
      encoders.put("MD4", new Md4PasswordEncoder());
      encoders.put("MD5", new MessageDigestPasswordEncoder("MD5"));
      encoders.put("noop", NoOpPasswordEncoder.getInstance());
      encoders.put("pbkdf2", new Pbkdf2PasswordEncoder());
      encoders.put("scrypt", new SCryptPasswordEncoder());
      encoders.put("SHA-1", new MessageDigestPasswordEncoder("SHA-1"));
      encoders.put("SHA-256", new MessageDigestPasswordEncoder("SHA-256"));
      encoders.put("sha256", new StandardPasswordEncoder());
      return new DelegatingPasswordEncoder(encodingId, encoders);
   }
   private PasswordEncoderFactories() {}
}

如此注入 PasswordEncoder 之后,我們在數(shù)據(jù)庫中需要這么存儲數(shù)據(jù):,后續(xù)就算修改了密碼加密方式,也不影響老數(shù)據(jù)的校驗(yàn)。

{bcrypt}$2a$10$dXJ3SW6G7P50lGmMkkmwe.20cQQubK3.HZWzG3YB1tlRy.fqvM/BG
{noop}password
{pbkdf2}5d923b44a6d129f3ddf3e3c8d29412723dcbde72445e8ef6bf3b508fbf17fa4ed4d6b99ca763d8dc
{scrypt}$e0801$8bWJaSu2IKSn9Z9kM+TPXfOc/9bdYSrN1oD9qfVThWEwdRTnO7re7Ei+fUZRJ68k9lTyuTeUp4of4g24hHnazw==$OAOec05+bXxvuu/1qZ6NUR+xQYvYv7BeL1QxwRpY5Pc=
{sha256}97cde38028ad898ebc02e690819fa220e88c62e0699403e94fff291cfffaf8410849f27605abcbc0

3, BCryptPasswordEncoder原理

$2b$[cost]$[22 character salt][31 character hash]
截圖.png
  • 上面例子中,2a 表示的hash算法的唯一標(biāo)志。這里表示的是Bcrypt算法。
  • 10 表示的是代價(jià)因子,這里是2的10次方,也就是1024輪。
  • N9qo8uLOickgx2ZMRZoMye 是16個字節(jié)(128bits)的salt經(jīng)過base64編碼得到的22長度的字符。
  • 最后的IjZAgcfl7p92ldGxad68LJZdL17lhWy是24個字節(jié)(192bits)的hash,經(jīng)過bash64的編碼得到的31長度的字符。
    \color{red}{說明加密密碼中存儲了salt,后面校驗(yàn)密碼的依據(jù)就是用用戶輸入的明文加上密文中的salt加密后再和密文比對。}

4, BCryptPasswordEncoder使用方式

PasswordEncoder passwordEncoder =  new BCryptPasswordEncoder();
String rawPassword = "123456";  //原始密碼
String encodedPassword = passwordEncoder.encode(rawPassword); //加密后的密碼
System.out.println("原始密碼" + rawPassword);
System.out.println("加密之后的hash密碼:" + encodedPassword);
System.out.println(rawPassword + "是否匹配" + encodedPassword + ":"   //密碼校驗(yàn):true
        + passwordEncoder.matches(rawPassword, encodedPassword));
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容