SpringBoot實(shí)現(xiàn)MD5加鹽算法的示例代碼
一、什么是加鹽算法
加鹽算法是一種用于增強(qiáng)密碼安全性的技術(shù)。這種技術(shù)通過(guò)在密碼存儲(chǔ)過(guò)程中添加一個(gè)隨機(jī)生成的鹽值(salt),來(lái)增加密碼被破解的難度。舉個(gè)例子:
在日常生活中,做菜是生活中的基本操作。炒青菜餐桌中必備的一道菜,不同的人炒青菜放鹽的程度是不同的,換句話(huà)來(lái)說(shuō)不同的人炒同一種菜放鹽的多與少是一種隨機(jī)的操作。因此,在注冊(cè)賬號(hào)時(shí),不同用戶(hù)在注冊(cè)時(shí)難免會(huì)使用相同的密碼。加鹽算法會(huì)根據(jù)不同的用戶(hù)生成一串隨機(jī)的字符串與用戶(hù)所輸入的密碼進(jìn)行結(jié)合生成一個(gè)最終的結(jié)果值,而這個(gè)最終值得到的就是加密后的密碼。
具體來(lái)說(shuō),加鹽加密的過(guò)程如下:
生成鹽值:后端在存儲(chǔ)一個(gè)密碼時(shí),首先會(huì)隨機(jī)生成一個(gè)鹽值。這個(gè)鹽值是一個(gè)隨機(jī)字符串,用于增加密碼的復(fù)雜性。
結(jié)合鹽值和密碼:將生成的鹽值和用戶(hù)輸入的密碼進(jìn)行有規(guī)則的結(jié)合,通常是將鹽值附加在密碼的前面或后面,或者通過(guò)其他方式進(jìn)行組合。
加密處理:將結(jié)合后的數(shù)據(jù)使用加密算法進(jìn)行加密。常見(jiàn)的加密算法包括MD5、SHA-256等。在Spring Security中,還可以使用更強(qiáng)大的加密方式,如BCrypt。本期講解使用MD5。
存儲(chǔ)加密后的數(shù)據(jù)和鹽值:將加密后的數(shù)據(jù)和鹽值按照一定規(guī)則組合起來(lái),并存儲(chǔ)在數(shù)據(jù)庫(kù)中。通常,鹽值會(huì)被保存在與加密密碼相同的記錄中,以便在驗(yàn)證用戶(hù)密碼時(shí)使用。
二、如何實(shí)現(xiàn)加鹽算法
2.1 加鹽算法代碼實(shí)現(xiàn)
首先,我們要了解到:
生成一個(gè)隨機(jī)鹽值可使用 UUID ,UUID 是一個(gè)128比特的數(shù)值,通常由 32 個(gè) 16 進(jìn)制數(shù)字組成,并以連字號(hào)分為五段,例如:550e8400-e29b-41d4-a716-446655440000,因此可使用 replace 方法去除 - 。
生成一個(gè) MD5 散列值,可使用 DigestUtils類(lèi) 中的 .md5DigestAsHex 方法將生成的隨機(jī)鹽值 salt 和 password 結(jié)合,并通過(guò) .getBytes 將結(jié)合后的字符串轉(zhuǎn)換成一個(gè)字節(jié)數(shù)組即 .getBytes(StandardCharsets.UTF_8) 。
此外需要注意的是:
- MD5(Message-Digest Algorithm 5)是一種廣泛使用的散列函數(shù),可以產(chǎn)生一個(gè)128位(16字節(jié))的散列值,通常表示為32位的十六進(jìn)制數(shù)。
- md5DigestAsHex 是 DigestUtils 類(lèi)中的一個(gè)靜態(tài)方法。這個(gè)方法接受一個(gè)字節(jié)數(shù)組作為輸入,計(jì)算其MD5散列值,并將結(jié)果轉(zhuǎn)換為十六進(jìn)制字符串。
- StandardCharsets.UTF_8 是一個(gè)表示UTF-8字符集的常量,它指定了字符串到字節(jié)數(shù)組的編碼方式。
代碼實(shí)現(xiàn):
/**
* 加密工具類(lèi)
*/
public class PasswordUtils {
public static String encrypt(String password){
// 1.鹽值
String salt = UUID.randomUUID().toString().replace("-","");
// 2.將鹽值+密碼進(jìn)行 md5 得到最終密碼
String finalPassword = DigestUtils.md5DigestAsHex((salt+password).getBytes(StandardCharsets.UTF_8));
// 3.將鹽值和最終密碼返回
return salt+"$"+finalPassword;
}
/**
* 加鹽驗(yàn)證
* @param password 待驗(yàn)證的密碼
* @param dbPassword 數(shù)據(jù)庫(kù)中的密碼:鹽值$最終密碼
* @return
*/
public static boolean decrypt(String password,String dbPassword) {
if(!StringUtils.hasLength(password) || !StringUtils.hasLength(dbPassword)
|| dbPassword.length() != 65) {
return false;
}
// 1.得到鹽值
String[] dbPasswordArray = dbPassword.split("\\$") ;
if (dbPasswordArray.length != 2) {
return false;
}
//鹽值
String salt = dbPasswordArray[0];
//最終正確密碼
String dbFinalPassword = dbPasswordArray[1];
// 2.加密待驗(yàn)證的密碼
String finalPassword = DigestUtils.md5DigestAsHex((salt+password).getBytes(StandardCharsets.UTF_8));
// 3.對(duì)比
if (finalPassword.equals(dbFinalPassword)) {
return true;
}
return false;
}
public static void main(String[] args) {
// 得到鹽值和最終密碼一共65位
System.out.println(encrypt("111"));
}
}在該類(lèi)中生成一個(gè) main 方法,測(cè)試最后生成的字符串符不符合預(yù)期,輸出結(jié)果:

將上述的字符串,定義為一個(gè)字符串,驗(yàn)證是否加密成功。
public static void main(String[] args) {
/* // 得到鹽值和最終密碼一共65位
System.out.println(encrypt("111"));*/
String dbPassword = "0f0d62e88c6c41dc83163e2813147111$b7321a14e1e5f3360a0d0d59e2b3cd6e";
System.out.println("當(dāng)密碼為123時(shí):"+decrypt("123",dbPassword));
System.out.println("當(dāng)密碼為111時(shí):"+decrypt("111",dbPassword));
}

2.2 注冊(cè)頁(yè)面中進(jìn)行密碼加鹽
/**
* 注冊(cè)
* @param userinfo
* @return
*/
@RequestMapping("/reg")
public ResultAjax reg(Userinfo userinfo) {
// 1.校驗(yàn)參數(shù)
if (userinfo == null || !StringUtils.hasLength(userinfo.getUsername()) ||
!StringUtils.hasLength(userinfo.getPassword())) {
return ResultAjax.fail(-1,"異常");
}
// 實(shí)現(xiàn)密碼加鹽
userinfo.setPassword(PasswordUtils.encrypt(userinfo.getPassword()));
// 2.請(qǐng)求接口 service 進(jìn)行添加接口
int ret = userService.reg(userinfo);
// 3.將結(jié)果返回給前端
return ResultAjax.success(ret);
}2.3 登錄頁(yè)面進(jìn)行加鹽的解密
/**
* 登錄
* @param userinfoVO
* @return
*/
@RequestMapping("/login")
public ResultAjax login(UserinfoVO userinfoVO, HttpServletRequest request) {
// 1.參數(shù)校驗(yàn)
if (userinfoVO == null || !StringUtils.hasLength(userinfoVO.getUsername())
|| !StringUtils.hasLength(userinfoVO.getPassword())) {
// 非法登錄
return ResultAjax.fail(-1,"非法登錄!");
}
// 2.根據(jù)用戶(hù)名查詢(xún)對(duì)象,判斷用戶(hù)名是否錯(cuò)誤
Userinfo userinfo = userService.getUserByName(userinfoVO.getUsername());
if (userinfo == null && userinfo.getId() == 0) {
return ResultAjax.fail(-2,"賬號(hào)或密碼錯(cuò)誤!");
}
// 3.使用對(duì)象中的密碼和輸入的密碼進(jìn)行對(duì)比,判斷密碼是否錯(cuò)誤
// 加鹽解密
if (!PasswordUtils.decrypt(userinfoVO.getPassword(),userinfo.getPassword())) {
return ResultAjax.fail(-2,"賬號(hào)或密碼錯(cuò)誤!");
}
// 4.成功后將對(duì)象存儲(chǔ)到 session 中
HttpSession session = request.getSession();
session.setAttribute(ApplicationVariable.SESSION_USERINFO_KEY,userinfo);
// 5.結(jié)果返回給用戶(hù)
return ResultAjax.success(1);
}此時(shí)查詢(xún)數(shù)據(jù)庫(kù)中,只有一條數(shù)據(jù),下面再注冊(cè)一個(gè)用戶(hù),觀(guān)察是否生成密碼。

2.4 注冊(cè)和登錄
注冊(cè)頁(yè)面

輸入用戶(hù)名 lisi 和密碼 123 后,提示注冊(cè)成功。

在數(shù)據(jù)中查詢(xún)對(duì)應(yīng)的數(shù)據(jù),驗(yàn)證密碼 123 被加秘為一大串字符串。

再來(lái)到登錄頁(yè)面,輸入用戶(hù)名 lisi 和密碼 123,提示登陸成功。

跳轉(zhuǎn)到個(gè)人頁(yè)面。

注冊(cè)頁(yè)面和登錄頁(yè)面的講解和源碼在前兩篇博客中已有詳細(xì)的講解,感興趣可以去看看。
到此這篇關(guān)于SpringBoot實(shí)現(xiàn)MD5加鹽算法的示例代碼的文章就介紹到這了,更多相關(guān)SpringBoot MD5加鹽算法內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
springboot報(bào)錯(cuò)Invalid?bound?statement?(not?found)的解決
本文主要介紹了springboot報(bào)錯(cuò)Invalid?bound?statement?(not?found)的解決,遇到這種問(wèn)題通常是沒(méi)有配置好配置文件,下面就來(lái)具體介紹一下解決方法,感興趣的可以了解一下2025-03-03
用Java程序判斷是否是閏年的簡(jiǎn)單實(shí)例
下面小編就為大家?guī)?lái)一篇用Java程序判斷是否是閏年的實(shí)現(xiàn)方法。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2016-06-06
基于GeoTools和SpringBoot的省域駕車(chē)最快路線(xiàn)規(guī)劃方法
本文探討基于GeoTools與SpringBoot的省域駕車(chē)最快路線(xiàn)規(guī)劃方法,解決復(fù)雜路況與地理因素影響,通過(guò)天地圖API計(jì)算并存儲(chǔ)距離數(shù)據(jù),展示湖南、新疆、黑龍江等地的路線(xiàn)成果,為交通與經(jīng)濟(jì)影響評(píng)估提供參考,感興趣的朋友快來(lái)一起學(xué)習(xí)吧2025-07-07
使用java實(shí)現(xiàn)BBS論壇發(fā)送郵件過(guò)程詳解
這篇文章主要介紹了使用java發(fā)送郵件過(guò)程詳解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2021-04-04
使用Java實(shí)現(xiàn)一個(gè)能保留計(jì)算過(guò)程的計(jì)算器
計(jì)算器是我們?nèi)粘I钪谐S玫墓ぞ咧?它能夠進(jìn)行基本的數(shù)學(xué)運(yùn)算,如加法、減法、乘法和除法,而在設(shè)計(jì)一個(gè)計(jì)算器時(shí),我們可以通過(guò)使用Java編程語(yǔ)言來(lái)實(shí)現(xiàn)一個(gè)簡(jiǎn)單的控制臺(tái)計(jì)算器,并且讓它能夠保留計(jì)算過(guò)程,文中有詳細(xì)的代碼示例,需要的朋友可以參考下2023-11-11
springBoot啟動(dòng)報(bào)錯(cuò)log4j沖突的解決方案
這篇文章主要介紹了springBoot啟動(dòng)報(bào)錯(cuò)log4j沖突的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。2021-07-07
Java手寫(xiě)線(xiàn)程池的實(shí)現(xiàn)方法
這篇文章主要為大家詳細(xì)介紹了Java手寫(xiě)線(xiàn)程池的實(shí)現(xiàn)方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-03-03
詳解Java中日期格式化的實(shí)現(xiàn)方法與潛在問(wèn)題解決
這篇文章主要為大家詳細(xì)介紹了Java中日期格式化的實(shí)現(xiàn)方法與潛在問(wèn)題解決的相關(guān)知識(shí),文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2025-11-11

