记一次使用BCryptPasswordEncoder,设置了不合理参数导致耗时严重的坑

记一次使用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:研究到这里就结束了,保住头发要紧。菜鸟开发第一次写博客,请各位看官轻喷~

hmoban主题是根据ripro二开的主题,极致后台体验,无插件,集成会员系统
自学咖网 » 记一次使用BCryptPasswordEncoder,设置了不合理参数导致耗时严重的坑