Spring Security 中的 BCryptPasswordEncoder加密、验证策略

首先明确一点,那就是 BCrypt 算法是一种 单向Hash加密算法

算法 特点 有效破解方式 破解难度 其它
对称加密 可以解密出明文 获取密钥 需要确保密钥不被泄露
单向Hash 不可解密 碰撞/彩虹表 可以通过加盐和多次hash来提高安全性,确保盐不被泄漏
Pbkdf2 不可解密 暂无 需要设定合理的参数

加密过程

直接上代码:以 spring-security-core-5.3.4.RELEASE-sources.jar 包中为例,在 BCryptPasswordEncoder.java 中,可以清晰的看到,有多个构造参数,构造参数重,有3个主要的参数:

  1. BCryptVersion 加密算法的版本,默认 $2A,除此之外可选的有$2Y和$2B;
  2. strength 个人理解的是单向hash的次数,默认值是10,可选4打31位;
  3. random安全的随机数生成器,不指定的话,就是 BCrypt.gensalt(version.getVersion(), strength) 方法来生成

上面这些都可以不用指定,有默认值。加密调用的方法是org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder#encode 。具体源码如下:

	public String encode(CharSequence rawPassword) {
		if (rawPassword == null) {
			throw new IllegalArgumentException("rawPassword cannot be null");
		}

		String salt;
		if (random != null) {
			salt = BCrypt.gensalt(version.getVersion(), strength, random);
		} else {
			salt = BCrypt.gensalt(version.getVersion(), strength);
		}
		return BCrypt.hashpw(rawPassword.toString(), salt);
	}

其中可以看出,关键的步骤在 BCrypt.hashpw(rawPassword.toString(), salt); 中。感兴趣具体的实现,可以再去对照源码进行分析。笔者更在意它生成后的存储格式,加密后的秘文格式如下:

Spring Security 中的 BCryptPasswordEncoder加密、验证策略

通常,这部分格式也是最终代码入库的部分。

解密过程

解密的思路,从已经加密后的秘文中,取出盐值,然后同用户输入的明文,进行BCrypt算法加密。将加密后的数据同数据库中存储的数据进行比较,如果相同,则认为密码输入正确,否则校验失败。具体的org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder#matches源码如下:

	public boolean matches(CharSequence rawPassword, String encodedPassword) {
		if (rawPassword == null) {
			throw new IllegalArgumentException("rawPassword cannot be null");
		}

		if (encodedPassword == null || encodedPassword.length() == 0) {
			logger.warn("Empty encoded password");
			return false;
		}

		if (!BCRYPT_PATTERN.matcher(encodedPassword).matches()) {
			logger.warn("Encoded password does not look like BCrypt");
			return false;
		}
		// 将明文同已经经过加盐+BCrypt算法加密后秘文进行比较
		return BCrypt.checkpw(rawPassword.toString(), encodedPassword);
	}

	/**
	 * Check that a password (as a byte array) matches a previously hashed
	 * one
	 * @param passwordb	the password to verify, as a byte array
	 * @param hashed	the previously-hashed password
	 * @return	true if the passwords match, false otherwise
	 * @since 5.3
	 */
	public static boolean checkpw(byte[] passwordb, String hashed) {
		return equalsNoEarlyReturn(hashed, hashpw(passwordb, hashed));
	}

个人感觉讲述的比较清晰资料

参考资料:

  1. Bcrypt加密之新认识 - 简书
  2. BCryptPasswordEncoder加密、验证策略_kevin-CSDN博客_bcryptpasswordencoder
上一篇:Go语言(Golang)密码加密存储(数据库中密码存储)


下一篇:序列化(serialization)