通常情况下,在新系统中使用BCrypt加密不需要考虑太多,但老系统由于存在大量旧数据,
草率接入会导致老用户无法登录,这种情况该怎么解决?
很简单,我们自己实现一个PasswordEncoder 并继承BCrpytPasswordEncoder即可。
@Component
public class MyPasswordEncoder extends BCryptPasswordEncoder {
private final Log logger = LogFactory.getLog(this.getClass());
// BCrypt 密文的正则表达式
private Pattern BCRYPT_PATTERN = Pattern.compile("\\A\\$2(a|y|b)?\\$(\\d\\d)\\$[./0-9A-Za-z]{53}");
@Override
public boolean matches(CharSequence rawPassword, String encodedPassword) {
if (rawPassword == null) {
throw new IllegalArgumentException("rawPassword cannot be null");
} else if (encodedPassword != null && encodedPassword.length() != 0) {
if (!this.BCRYPT_PATTERN.matcher(encodedPassword).matches()) {
// TODO 如果密码不是BCrypt 密文 do something
return ...;
} else {
return BCrypt.checkpw(rawPassword.toString(), encodedPassword);
}
} else {
this.logger.warn("Empty encoded password");
return false;
}
}
}
在这个PasswordEncoder中,只有当密码不是BCrypt密文时,才启用自定义的匹配逻辑,
其余还是沿用原来的方案,即可轻松达到兼容的目的。
再进一步,如果我们不仅想要兼容,还想将不安全的旧密码无缝修改成BCrypt密文,该如何操作呢?这是个很好的问题。
如果旧密码都是未经任何加密的明文,也许“跑库”修改是非常好的一种选择,但并非所有系统都有这么理想的状态。
假如旧密码都是被散列加密过的,那么可以使用下列两种选择
- 使用增量更新的方法。当用户输入的密码正确时,判断数据库中的密码是否为BCrypt密文,如果不是,则尝试使用用户输入的密码重新生成BCrpyt密文并写回数据库。
- 以旧的加密方案作为基础接入BCrpyt加密,eg: 旧的方案是MD5加密,即数据库中的所有密码都是MD5形式的密码,那么直接把这些密码当作明文,先“跑库”生成BCrypt密文,再使用encode和matches两个方法在执行BCrypt加密之前都先用MD5运算一遍即可。