广告位联系
返回顶部
分享到

Spring security BCryptPasswordEncoder密码验证原理的介绍

java 来源:互联网搜集 作者:秩名 发布时间:2020-03-07 19:51:34 人浏览
摘要

一、加密算法和hash算法的区别 加密算法是一种可逆的算法,基本过程就是对原来为明文的文件或数据按某种算法进行处理,使其成为不可读的一段代码为密文,但在用相应的密钥进行操作之后就可以得到原来的内容 。 哈希算法是一种不可逆的算法,是把任意长度的输

一、加密算法和hash算法的区别

加密算法是一种可逆的算法,基本过程就是对原来为明文的文件或数据按某种算法进行处理,使其成为不可读的一段代码为“密文”,但在用相应的密钥进行操作之后就可以得到原来的内容 。

哈希算法是一种不可逆的算法,是把任意长度的输入通过散列算法变换成固定长度的输出,输出就是散列值,不同的输入可能会散列成相同的输出,所以不可能从散列值来确定唯一的输入值。

二、源码解析

BCryptPasswordEncoder类实现了PasswordEncoder接口,这个接口中定义了两个方法

?
1
2
3
4
public interface PasswordEncoder {
  String encode(CharSequence rawPassword);
  boolean matches(CharSequence rawPassword, String encodedPassword);
}

其中encode(...)是对字符串进行加密的方法,matches使用来校验传入的明文密码rawPassword是否和加密密码encodedPassword相匹配的方法。即对密码进行加密时调用encode,登录认证时调用matches

下面我们来看下BCryptPasswordEncoder类中这两个方法的具体实现

1. encode方法

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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方法

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
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方法用来判断一个明文是否和一个加密字符串对应。

?
1
2
3
4
5
6
7
8
9
10
11
12
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,第一个参数是明文,第二个参数是加密后的字符串。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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用来加密,保证了这一点。


版权声明 : 本文内容来源于互联网或用户自行发布贡献,该文观点仅代表原作者本人。本站仅提供信息存储空间服务和不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权, 违法违规的内容, 请发送邮件至2530232025#qq.cn(#换@)举报,一经查实,本站将立刻删除。
原文链接 : https://www.cnblogs.com/chengxuxiaoyuan/p/11939084.html
相关文章
  • SpringBoot自定义错误处理逻辑介绍

    SpringBoot自定义错误处理逻辑介绍
    1. 自定义错误页面 将自定义错误页面放在 templates 的 error 文件夹下,SpringBoot 精确匹配错误信息,使用 4xx.html 或者 5xx.html 页面可以打印错误
  • Java实现手写一个线程池的代码

    Java实现手写一个线程池的代码
    线程池技术想必大家都不陌生把,相信在平时的工作中没有少用,而且这也是面试频率非常高的一个知识点,那么大家知道它的实现原理和
  • Java实现断点续传功能的代码

    Java实现断点续传功能的代码
    题目实现:网络资源的断点续传功能。 二、解题思路 获取要下载的资源网址 显示网络资源的大小 上次读取到的字节位置以及未读取的字节
  • 你可知HashMap为什么是线程不安全的
    HashMap 的线程不安全 HashMap 的线程不安全主要体现在下面两个方面 在 jdk 1.7 中,当并发执行扩容操作时会造成环形链和数据丢失的情况 在
  • ArrayList的动态扩容机制的介绍

    ArrayList的动态扩容机制的介绍
    对于 ArrayList 的动态扩容机制想必大家都听说过,之前的文章中也谈到过,不过由于时间久远,早已忘却。 所以利用这篇文章做做笔记,加
  • JVM基础之字节码的增强技术介绍

    JVM基础之字节码的增强技术介绍
    字节码增强技术 在上文中,着重介绍了字节码的结构,这为我们了解字节码增强技术的实现打下了基础。字节码增强技术就是一类对现有字
  • Java中的字节码增强技术

    Java中的字节码增强技术
    1.字节码增强技术 字节码增强技术就是一类对现有字节码进行修改或者动态生成全新字节码文件的技术。 参考地址 2.常见技术 技术分类 类
  • Redis BloomFilter布隆过滤器原理与实现

    Redis BloomFilter布隆过滤器原理与实现
    Bloom Filter 概念 布隆过滤器(英语:Bloom Filter)是1970年由一个叫布隆的小伙子提出的。它实际上是一个很长的二进制向量和一系列随机映射
  • Java C++算法题解leetcode801使序列递增的最小交换次

    Java C++算法题解leetcode801使序列递增的最小交换次
    题目要求 思路:状态机DP 实现一:状态机 Java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 class Solution { public int minSwap(int[] nums1, int[] nums2) { int n
  • Mybatis结果集映射与生命周期介绍

    Mybatis结果集映射与生命周期介绍
    一、ResultMap结果集映射 1、设计思想 对简单的语句做到零配置,对于复杂一点的语句,只需要描述语句之间的关系就行了 2、resultMap的应用场
  • 本站所有内容来源于互联网或用户自行发布,本站仅提供信息存储空间服务,不拥有版权,不承担法律责任。如有侵犯您的权益,请您联系站长处理!
  • Copyright © 2017-2022 F11.CN All Rights Reserved. F11站长开发者网 版权所有 | 苏ICP备2022031554号-1 | 51LA统计