spring security的BCryptPasswordEncoder加密和对密码验证的原理
BCryptPasswordEncoder加密和对密码验证的原理
上一篇:spring security进阶2 添加账户并对账户密码进行加密
spring security中提供了一个加密类BCryptPasswordEncoder,可以用来对密码字符串进行加密,得到加密后的字符串。它采用哈希算法 SHA-256 +随机盐+密钥对密码进行加密
一、加密算法和hash算法的区别
加密算法是一种可逆的算法,基本过程就是对原来为明文的文件或数据按某种算法进行处理,使其成为不可读的一段代码为“密文”,但在用相应的密钥进行操作之后就可以得到原来的内容 。
哈希算法是一种不可逆的算法,是把任意长度的输入通过散列算法变换成固定长度的输出,输出就是散列值,不同的输入可能会散列成相同的输出,所以不可能从散列值来确定唯一的输入值。
二、源码解析
BCryptPasswordEncoder类实现了PasswordEncoder接口,这个接口中定义了两个方法
public interface PasswordEncoder {
String encode(CharSequence rawPassword);
boolean matches(CharSequence rawPassword, String encodedPassword);
}
其中encode(...)是对字符串进行加密的方法,matches使用来校验传入的明文密码rawPassword是否和加密密码encodedPassword相匹配的方法。即对密码进行加密时调用encode,登录认证时调用matches
下面我们来看下BCryptPasswordEncoder类中这两个方法的具体实现
1. encode方法
public String encode(CharSequence rawPassword) {
String salt;
if (strength > 0) {
if (random != null) {
salt = BCrypt.gensalt(strength, random);
}
else {
salt = BCrypt.gensalt(strength);
}
}
else {
salt = BCrypt.gensalt();
}
return BCrypt.hashpw(rawPassword.toString(), salt);
}
可以看到,这个方法中先基于某种规则得到了一个盐值,然后在调用BCrypt.hashpw方法,传入明文密码和盐值salt。所以我们再看下BCrypt.hashpw方法中做了什么
2. BCrypt.hashpw方法
public static String hashpw(String password, String salt) throws IllegalArgumentException {
BCrypt B;
String real_salt;
byte passwordb[], saltb[], hashed[];
char minor = (char) 0;
int rounds, off = 0;
StringBuilder rs = new StringBuilder();
if (salt == null) {
throw new IllegalArgumentException("salt cannot be null");
}
int saltLength = salt.length();
if (saltLength < 28) {
throw new IllegalArgumentException("Invalid salt");
}
if (salt.charAt(0) != '$' || salt.charAt(1) != '2') {
throw new IllegalArgumentException("Invalid salt version");
}
if (salt.charAt(2) == '$') {
off = 3;
}
else {
minor = salt.charAt(2);
if (minor != 'a' || salt.charAt(3) != '$') {
throw new IllegalArgumentException("Invalid salt revision");
}
off = 4;
}
if (saltLength - off < 25) {
throw new IllegalArgumentException("Invalid salt");
}
// Extract number of rounds
if (salt.charAt(off + 2) > '$') {
throw new IllegalArgumentException("Missing salt rounds");
}
rounds = Integer.parseInt(salt.substring(off, off + 2));
real_salt = salt.substring(off + 3, off + 25);
try {
passwordb = (password + (minor >= 'a' ? "\000" : "")).getBytes("UTF-8");
}
catch (UnsupportedEncodingException uee) {
throw new AssertionError("UTF-8 is not supported");
}
saltb = decode_base64(real_salt, BCRYPT_SALT_LEN);
B = new BCrypt();
hashed = B.crypt_raw(passwordb, saltb, rounds);
rs.append("$2");
if (minor >= 'a') {
rs.append(minor);
}
rs.append("$");
if (rounds < 10) {
rs.append("0");
}
rs.append(rounds);
rs.append("$");
encode_base64(saltb, saltb.length, rs);
encode_base64(hashed, bf_crypt_ciphertext.length * 4 - 1, rs);
return rs.toString();
}
可以看到,这个方法中先根据传入的盐值salt,然后基于某种规则从salt得到real_salt,后续的操作都是用这个real_salt来进行,最终得到加密字符串。
所以这里有一个重点:传入的盐值salt并不是最终用来加密的盐,方法中通过salt得到了real_salt,记住这一点,因为后边的匹配方法matches中要用到这一点。
3. matches方法
matches方法用来判断一个明文是否和一个加密字符串对应。
public boolean matches(CharSequence rawPassword, String encodedPassword) {
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;
}
return BCrypt.checkpw(rawPassword.toString(), encodedPassword);
}
这个方法中先对密文字符串进行了一些校验,如果不符合规则直接返回不匹配,然后调用校验方法BCrypt.checkpw,第一个参数是明文,第二个参数是加密后的字符串。
public static boolean checkpw(String plaintext, String hashed) {
return equalsNoEarlyReturn(hashed, hashpw(plaintext, hashed));
}
static boolean equalsNoEarlyReturn(String a, String b) {
char[] caa = a.toCharArray();
char[] cab = b.toCharArray();
if (caa.length != cab.length) {
return false;
}
byte ret = 0;
for (int i = 0; i < caa.length; i++) {
ret |= caa[i] ^ cab[i];
}
return ret == 0;
}
注意 equalsNoEarlyReturn(hashed, hashpw(plaintext, hashed))这里,第一个参数是加密后的字符串,而第二个参数是用刚才提过的hashpw方法对明文字符串进行加密。
hashpw(plaintext, hashed)第一个参数是明文,第二个参数是加密字符串,但是在这里是作为盐值salt传入的,所以就用到了刚才说的 hashpw 内部通过传入的salt得到real_salt,这样就保证了对现在要校验的明文的加密和得到已有密文的加密用的是同样的加密策略,算法和盐值都相同,这样如果新产生的密文和原来的密文相同,则这两个密文对应的明文字符串就是相等的。
这也说明了加密时使用的盐值被写在了最终生成的加密字符串中。
三、总结
BCryptPasswordEncoder使用哈希算法+随机盐来对字符串加密。因为哈希是一种不可逆算法,所以密码认证时需要使用相同的算法+盐值来对待校验的明文进行加密,然后比较这两个密文来进行验证。BCryptPasswordEncoder在加密时通过从传入的salt中获取real_salt用来加密,保证了这一点。
spring security的BCryptPasswordEncoder加密和对密码验证的原理的更多相关文章
- spring security +MySQL + BCryptPasswordEncoder 单向加密验证 + 权限拦截 --- 心得
1.前言 前面学习了 security的登录与登出 , 但是用户信息 是 application 配置 或内存直接注入进去的 ,不具有实用性,实际上的使用还需要权限管理,有些 访问接口需要某些权限才可 ...
- Spring Security项目的搭建以及Spring Security的BCrypt加密
.personSunflowerP { background: rgba(51, 153, 0, 0.66); border-bottom: 1px solid rgba(0, 102, 0, 1); ...
- spring boot:spring security整合jwt实现登录和权限验证(spring boot 2.3.3)
一,为什么使用jwt? 1,什么是jwt? Json Web Token, 它是JSON风格的轻量级的授权和身份认证规范, 可以实现无状态.分布式的Web应用授权 2,jwt的官网: https:// ...
- spring security (BCryptPasswordEncoder)加密及判断密码是否相同
通过BCryptPasswordEncoder的加密的相同字符串的结果是不同的,如果需要判断是否是原来的密码,需要用它自带的方法. 加密: BCryptPasswordEncoder encode = ...
- Spring Security 中的加密BCryptPasswordEncoder
// // Source code recreated from a .class file by IntelliJ IDEA // (powered by Fernflower decompiler ...
- BCryptPasswordEncoder加密及判断密码是否相同
项目中用到了BCryptPasswordEncoder对密码进行二次加密,需要注意的是,加密后的字符串比较长,数据库的长度至少为60位. 通过BCryptPasswordEncoder的加密的相同字符 ...
- spring security实现记住我下次自动登录功能
目录 spring security实现记住我下次自动登录功能 一.原理分析 二.实现方式 2.1 简单实现方式 2.2 数据库实现方式 三.区分是密码登录还是rememberme登录 spring ...
- 255.Spring Boot+Spring Security:使用md5加密
说明 (1)JDK版本:1.8 (2)Spring Boot 2.0.6 (3)Spring Security 5.0.9 (4)Spring Data JPA 2.0.11.RELEASE (5)h ...
- spring security进阶2 添加账户并对账户密码进行加密
目录 spring security 添加账户并对账户密码进行加密 一.原理分析 1.1加密原理 1.2加密后的登录过程 二.代码实现 2.1添加用户的页面如下, register.html 2.2c ...
随机推荐
- robot framework 笔记(四),使用时遇到的问题
背景: 使用rf遇到的一些问题汇总 一:跑WEBUI的时候报错: [ WARN ] Keyword 'Capture Page Screenshot' could not be run on fail ...
- 在vue项目中使用自己封装的ajax
在 src 目录下新建 vue.extend.js ,内容如下: export default { install(Vue) { Vue.prototype.$http=function(option ...
- test20190909 Gluttony
0+0+0+0+0+0=0.毒瘤出题人. BJOI2019 勘破神机 地灾军团的军师黑袍从潜伏在精灵高层的密探手中得知了神杖的情报,他对奥术宝石中蕴含的远古神秘力量十分感兴趣.他设计夺取了数块奥术宝石 ...
- STM32 IAP程序 源码 和测试代码 有详细的中文注释
http://bbs.21ic.com/forum.php?mod=viewthread&tid=588265&reltid=624002&pre_pos=2&ext= ...
- CollectionUtils.select用法
import java.util.ArrayList;import java.util.List; import org.apache.commons.collections.CollectionUt ...
- 洛谷 题解 UVA572 【油田 Oil Deposits】
这是我在洛谷上的第一篇题解!!!!!!!! 这个其实很简单的 我是一只卡在了结束条件这里所以一直听取WA声一片,详细解释代码里见 #include<iostream> #include&l ...
- mysql round()函数以及convert()函数,保留n位小数
mysql> ); +----------------+ | round() | +----------------+ | 2.23 | +----------------+ row in se ...
- Linux 把进程为D(不可中断进程)转换成其他状态
问题现象:当前集群跑hadoop的时候,,任务失败,但是跑任务的容器没有正常退出,显示一大堆的YarcChild进程,,more /proc/进程/status 查看其状态,进程为D(disk s ...
- 高斯混合模型(GMM)及MATLAB代码
之前在学习中遇到高斯混合模型,卡了很长一段时间,在这里记下学习中的一些问题以及解决的方法.希望看到这篇文章的同学们对高斯混合模型能有一些基本的概念.全文不废话,直接上重点. 本文将从以下三个问题详解高 ...
- 解决WordPress访问中文标签出现404的几个方法
最近很多主题用户提到安装完WordPress后中文标签出现404的情况,出现这种情况一般修改固定链接设置是没有效果的,多数是windows主机带来的麻烦.网上多数人说要修改核心文件class-wp.p ...