点进来你就是我的人了
博主主页:戳一戳,欢迎大佬指点!欢迎志同道合的朋友一起加油喔
目录
一、什么是哈希加密?
二、哈希加密的问题
三、加盐哈希加密
四、Java中的加盐哈希加密实践
1. 加盐加密代码实例
2. 验证加盐加密的密码代码实例
五、MD5加密流程对比加盐加密流程
MD5加密流程:
加盐加密流程:
六、spring框架实现加盐加密
在当今的信息时代,数据安全的重要性不言而喻。我们的系统中包含了许多敏感数据,其中就包括用户的密码。尽管密码是敏感的,但直接存储明文密码会导致严重的安全问题。因此,对密码进行哈希加密以提供更好的安全性是常见的做法。在这篇文章中,我将带你深入理解加盐哈希加密算法,并演示如何在Java中实现它。
哈希加密是一种通过哈希函数对输入数据进行转换,输出一个固定长度的字符串的过程。这个输出称为哈希值或者哈希码。哈希函数具有以下特性:
对于密码存储来说,哈希加密非常有用。我们将用户的明文密码通过哈希函数转换为哈希值,然后将这个哈希值存储在数据库中,而非明文密码。在验证用户身份时,我们只需要将用户输入的密码经过同样的哈希函数,然后比较哈希值是否一致即可。
然而,纯粹的哈希加密也有其弱点。如果两个用户使用相同的密码,那么他们的哈希值也将相同。这就意味着,如果一个用户的哈希密码被泄露,那么使用相同密码的其他用户也会受到威胁。此外,由于哈希函数是公开的,黑客可以通过“彩虹表”攻击预先计算并存储哈希值与原始密码的对应关系,从而反推出明文密码。
彩虹表是一种用于破解哈希函数的预计算表。它包含了许多预计算的哈希值和对应的原始数据(密码)。攻击者可以通过比对彩虹表,找出与泄露的哈希值相匹配的原始数据,从而破解出原始密码。这种攻击方式对于那些使用简单密码的用户尤其有效,因为这些简单密码很可能已经被包含在彩虹表中。
为什么说一个盐值对应一个彩虹表呢?
接下来,我将以Java为例,介绍如何实现加盐加密和验证加盐加密密码。
/**
* 加盐加密
* @param password 明文密码
* @return 加盐加密的密码
*/
public static String encrypt(String password) {
// 这是一个静态方法,输入的参数是你想要加密的原始密码。
//1.产生盐值
String salt = UUID.randomUUID().toString().replace("-","");
// UUID.randomUUID().toString() 用于生成一个随机的唯一识别码,这个码的格式是一个32位的数字,包含四个"-"。
// replace("-","") 是为了将这些"-"去除,得到一个完全由数字和字母组成的32位字符串,这就是所谓的"盐值"。
//2.使用(盐值+明文密码) 得到加密的密码
String finalPassword = DigestUtils.md5DigestAsHex((salt + password).getBytes());
// salt + password 是将盐值和密码拼接在一起,getBytes() 方法是将拼接后的字符串转化为字节数组,以便后面进行哈希运算。
// DigestUtils.md5DigestAsHex 是Spring框架提供的一个工具类,用于进行MD5哈希运算,并将结果转化为16进制的字符串。
// 这样我们就得到了加盐后的密码。
//3.将盐值和加密的密码共同返回(合并盐值和加密码)
String dbPassword = salt + "$" + finalPassword;
// 最后,我们将盐值、一个"$"和加密后的密码拼接在一起,这样在验证密码时,我们可以知道盐值是什么。
// "$"在这里是作为盐值和密码之间的分隔符,也可以选择其他不会在盐值和密码中出现的字符。
return dbPassword;
// 将最终的字符串返回,这就是存入数据库的密码。
}
通过这种方法,即使两个用户的原始密码相同,他们在数据库中存储的密码也会不同,因为盐值是随机生成的。同时,由于盐值是与密码一同存储的,所以在验证密码时也能知道盐值是什么,从而正确地验证密码。
/**
* 验证加盐加密密码
* @param password 明文密码(不一定对, 需要验证明文密码)
* @param dbPassword 数据库存储的密码(包含: salt+$+加盐加密密码)
* @return
*/
public static boolean decrypt(String password, String dbPassword) {
// 这个方法是静态方法,输入参数是用户输入的待验证密码和数据库中存储的密码。方法将返回一个布尔值,表示密码是否正确。
boolean result = false;
// 默认的结果是 false,也就是默认密码是错误的。
if(StringUtils.hasLength(password) && StringUtils.hasLength(dbPassword)
&& dbPassword.length() == 65 && dbPassword.contains("$")) { //参数正确
// 这个if语句是进行一些基本的参数检查,检查输入的密码和数据库中存储的密码是否都不为空,
// 数据库中存储的密码长度是否是65(因为盐值长度是32,加密后的密码长度也是32,再加上一个"$",总长度就是65),
// 以及数据库中存储的密码是否包含"$"。
//1. 得到盐值
String[] passwordArr = dbPassword.split("\\$");
// 使用split方法将数据库中的密码以"$"为分隔符分割成两部分,第一部分是盐值,第二部分是加盐后的密码。
//1.1 盐值
String salt = passwordArr[0];
// 获取盐值,这是分割后的第一部分。
//1.2 得到正确密码的加盐加密密码
String finalPassword = passwordArr[1];
// 获取加盐后的密码,这是分割后的第二部分。
//2. 生成验证密码的加盐加密密码
String checkPassword = encrypt(password, salt);
// 使用用户输入的待验证密码和盐值进行同样的加密操作,得到待验证的加盐后的密码。
if(dbPassword.equals(checkPassword)) {
result = true;
}
// 如果待验证的加盐后的密码和数据库中存储的加盐后的密码相等,那么说明密码正确,将结果设为true。
}
return result;
// 返回验证结果。
}
这种方法的优点是即使攻击者拿到了数据库中存储的密码和盐值,也无法直接知道原始的密码是什么,因为加盐后的密码和原始的密码是通过不可逆的哈希函数得到的。只有知道了原始的密码,才能通过同样的加盐和哈希操作得到同样的结果,从而验证密码是否正确。
用户在输入密码后,系统首先将用户的密码作为输入参数传给MD5函数。
MD5函数会对输入进行一系列复杂的数学运算,生成一个128位的哈希值,也就是我们所说的MD5值。
这个MD5值通常以32位的十六进制数表示,我们将这个值存储在数据库中。
当用户下次登录时,系统会再次将输入的密码进行MD5运算,并将得到的MD5值与存储在数据库中的值进行比较。如果两者相同,则验证成功,用户登录成功。
首先,系统会生成一个随机的字符串,我们称之为"盐"。
然后,系统会将用户输入的密码和这个盐值拼接在一起,形成一个新的字符串。
这个新的字符串会作为输入参数传给哈希函数,通常我们仍然可以使用MD5函数或者其他更安全的哈希函数如SHA-256。
哈希函数会对这个新的字符串进行哈希运算,得到一个哈希值。我们将这个哈希值和盐值一起存储在数据库中。
当用户下次登录时,系统会将用户输入的密码和存储在数据库中的盐值进行拼接,并进行哈希运算。然后将得到的哈希值与存储在数据库中的哈希值进行比较。如果两者相同,则验证成功,用户登录成功。
加盐加密在MD5的基础上增加了一个随机的盐值,这样就使得即使两个用户的密码相同,他们的哈希值也会因为盐值的不同而不同。这大大增加了破解的难度,因为攻击者现在需要针对每一个盐值都进行一次攻击,而不能像攻击MD5那样只需要一次攻击就可以破解所有密码。
Spring Security是Spring框架下的一个子框架,它主要负责应用程序的安全性,包括身份验证和授权。在Spring Security中,有一个类BCryptPasswordEncoder
,可以用于使用加盐的BCrypt算法对密码进行哈希。
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
public class PasswordEncoderUtil {
private static BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
//加密密码
public static String encodePassword(String rawPassword) {
return passwordEncoder.encode(rawPassword);
}
// 验证密码
public static boolean matches(String rawPassword, String encodedPassword) {
return passwordEncoder.matches(rawPassword, encodedPassword);
}
}
在上述代码中,
BCryptPasswordEncoder
类用于创建基于BCrypt的加盐哈希密码。在创建对象时,BCryptPasswordEncoder会自动在每次加密时生成一个随机盐,并将其合并到哈希值中。对于相同的明文,每次生成的加密文本都会不同。在密码验证时,
matches()
方法会自动从存储的哈希值中提取盐,与提供的明文密码一起进行哈希运算,并将结果与存储的哈希值进行比较。在使用上述工具类对密码进行加密后,你可以将加密后的密码存储在数据库中。当需要验证密码时,只需调用
matches()
方法进行验证即可。
下面是如何使用Spring Security进行加盐哈希密码的步骤解析:
1. 在你的项目中,需要先在POM.xml文件或build.gradle中加入Spring Security的依赖。对于Maven项目,你可以在POM.xml中添加以下代码:
org.springframework.boot
spring-boot-starter-security
2. 创建一个BCryptPasswordEncoder
对象:
BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
3. 你可以使用BCryptPasswordEncoder
对象的encode
方法对密码进行哈希:
String hashedPassword = passwordEncoder.encode(plainPassword);
其中plainPassword
是原始的未加密的密码。encode
方法会对原始密码进行BCrypt哈希,并添加一个随机的盐值。然后,你可以将hashedPassword
保存到数据库中。
4. 当需要验证密码时,你可以使用BCryptPasswordEncoder
对象的matches
方法:
boolean isPasswordMatch = passwordEncoder.matches(rawPassword, hashedPassword);
其中rawPassword
是用户输入的密码,hashedPassword
是存储在数据库中的哈希密码。matches
方法会先将存储在哈希密码中的盐值添加到用户输入的密码上,然后进行哈希,最后将哈希后的结果和存储在数据库中的哈希密码进行比较。如果两者相同,那么matches
方法将返回true
,否则返回false
。
注意,Spring Security使用的BCrypt算法已经内置了添加和使用盐值的机制,你不需要自己生成和存储盐值。这大大简化了使用加盐哈希密码的流程。
如果你觉得这篇文章有价值,或者你喜欢它,那么请点赞并分享给你的朋友。你的支持是我创作更多有用内容的动力,感谢你的阅读和支持。祝你有个美好的一天!