記一次使用BCryptPasswordEncoder,設置了不合理參數導致耗時嚴重的坑


導讀

在項目開發中,越來越重視安全相關的功能。在使用Spring Boot進行項目開發的時候,使用Spring Security框架是一個不錯的選擇。
開發登錄認證功能的時候,一般情況都不會將原始密碼明文存儲到數據庫中,那么就需要對密碼進行加密,Spring Security推薦使用的是BCryptPasswordEncoder,說明它有可取之處。

問題

問題:在登錄認證的時候,每次均需耗費5S以上的時間,這是用戶無法忍受的事情。
排查過程:通過visualVM 的線程Dump的信息發現,在自定義的認證過濾器中的attemptAuthentication()方法進入認證之后,在等待驗證密碼成功返回的時間最長。將目標放在密碼比對的方法上,由於注入的是BCryptPasswordEncoder,找到它調用的比對方法matches(),傳入原始密碼和數據庫中的密碼,返回是否匹配的布爾值。
既然發現目標了,那就去扣一扣它的源碼吧。

BCryptPasswordEncoder.class

/**
 * Implementation of PasswordEncoder that uses the BCrypt strong hashing function. Clients
 * can optionally supply a "version" ($2a, $2b, $2y) and a "strength" (a.k.a. log rounds
 * in BCrypt) and a SecureRandom instance. The larger the strength parameter the more work
 * will have to be done (exponentially) to hash the passwords. The default value is 10.
 *
 * @author Dave Syer
 */
	private Pattern BCRYPT_PATTERN = Pattern.compile("\\A\\$2(a|y|b)?\\$(\\d\\d)\\$[./0-9A-Za-z]{53}");
	private final int strength;
	private final BCryptVersion version;
	private final SecureRandom random;

通過注釋,不難發現,生成的密碼字符串有一定的規律,構造方法里面可選參數有三個,strength,version,random
參數strength:默認值為10,可選值為4-31;
參數version:默認值為$2a,可選值為$2a, $2b, $2y;
參數random:SecureRandom的實例,默認值為空;

該類中生成密碼的方法encode()需要傳入原始密碼字符串,調用算法實現類BCrypthashpw()方法,增加一個salt參數,該參數的值由BCryptgensalt()結合strength, version, random生成。
進行密碼比較,也就是調用matches()方法的時候,首先需要將用戶上傳的密碼(原始密碼)進行加密,因此可以得出耗時的操作在於將原始密碼加密。

當需要加密的密碼相同時,可能影響性能的因素就只有strength, version, random,使用控制變量法來進行測試,但是參數random沒有太大測試的意義。
1.改變參數strength的值,保持其余兩個值為默認值。

// strength設置為10,也就是默認值時
String password = new BCryptPasswordEncoder(10).encode("password");
System.out.println(password);

image

// strength設置為16的時候,不要問我為什么測試16,問就是我當時手抽設置的就是16
String password = new BCryptPasswordEncoder(16).encode("password");
System.out.println(password);

image

// strength設置為20的時候
String password = new BCryptPasswordEncoder(20).encode("password");
System.out.println(password);

image

由於strength數值越大,耗時越嚴重,后面的值不測試了(因為測試了25,運行了19分鍾都沒有出來)。

2.改變參數version的值,保持其余兩個值為默認值。

參數version設置為$2a,由於是默認值,故同測試1中的第二個用例。

// versionh設置為$2b
String password = new BCryptPasswordEncoder(BCryptPasswordEncoder.BCryptVersion.$2B, 16).encode("password");
System.out.println(password);

image

// version設置為$2y
String password = new BCryptPasswordEncoder(BCryptPasswordEncoder.BCryptVersion.$2Y, 16).encode("password");
System.out.println(password);

image

總結

通過以上測試可以看出,參數strength的值改變對於生成加密密碼的時間影響是最大的。

為甚麽記錄這篇隨筆?希望對遇到同樣問題的你有所幫助。

PS:研究到這里就結束了,保住頭發要緊。菜鳥開發第一次寫博客,請各位看官輕噴~


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM