目錄
一、什么是加鹽算法
二、如何實現加鹽算法
2.1 加鹽算法代碼實現
2.2 注冊頁面中進行密碼加鹽
2.3 登錄頁面進行加鹽的解密
2.4 注冊和登錄
一、什么是加鹽算法
加鹽算法是一種用于增強密碼安全性的技術。這種技術通過在密碼存儲過程中添加一個隨機生成的鹽值(salt),來增加密碼被破解的難度。舉個例子:
在日常生活中,做菜是生活中的基本操作。炒青菜餐桌中必備的一道菜,不同的人炒青菜放鹽的程度是不同的,換句話來說不同的人炒同一種菜放鹽的多與少是一種隨機的操作。因此,在注冊賬號時,不同用戶在注冊時難免會使用相同的密碼。加鹽算法會根據不同的用戶生成一串隨機的字符串與用戶所輸入的密碼進行結合生成一個最終的結果值,而這個最終值得到的就是加密后的密碼。
具體來說,加鹽加密的過程如下:
- 生成鹽值:后端在存儲一個密碼時,首先會隨機生成一個鹽值。這個鹽值是一個隨機字符串,用于增加密碼的復雜性。
- 結合鹽值和密碼:將生成的鹽值和用戶輸入的密碼進行有規則的結合,通常是將鹽值附加在密碼的前面或后面,或者通過其他方式進行組合。
- 加密處理:將結合后的數據使用加密算法進行加密。常見的加密算法包括MD5、SHA-256等。在Spring Security中,還可以使用更強大的加密方式,如BCrypt。本期講解使用MD5。
- 存儲加密后的數據和鹽值:將加密后的數據和鹽值按照一定規則組合起來,并存儲在數據庫中。通常,鹽值會被保存在與加密密碼相同的記錄中,以便在驗證用戶密碼時使用。
二、如何實現加鹽算法
2.1 加鹽算法代碼實現
首先,我們要了解到:
生成一個隨機鹽值可使用 UUID ,UUID 是一個128比特的數值,通常由 32 個 16 進制數字組成,并以連字號分為五段,例如:550e8400-e29b-41d4-a716-446655440000,因此可使用 replace 方法去除 - 。
生成一個 MD5 散列值,可使用 DigestUtils類 中的 .md5DigestAsHex 方法將生成的隨機鹽值 salt 和 password 結合,并通過 .getBytes 將結合后的字符串轉換成一個字節數組即 .getBytes(StandardCharsets.UTF_8) 。
此外需要注意的是:
- MD5(Message-Digest Algorithm 5)是一種廣泛使用的散列函數,可以產生一個128位(16字節)的散列值,通常表示為32位的十六進制數。
- md5DigestAsHex 是?DigestUtils 類中的一個靜態方法。這個方法接受一個字節數組作為輸入,計算其MD5散列值,并將結果轉換為十六進制字符串。
- StandardCharsets.UTF_8?是一個表示UTF-8字符集的常量,它指定了字符串到字節數組的編碼方式。
代碼實現:
/*** 加密工具類*/
public class PasswordUtils {public static String encrypt(String password){// 1.鹽值String salt = UUID.randomUUID().toString().replace("-","");// 2.將鹽值+密碼進行 md5 得到最終密碼String finalPassword = DigestUtils.md5DigestAsHex((salt+password).getBytes(StandardCharsets.UTF_8));// 3.將鹽值和最終密碼返回return salt+"$"+finalPassword;}/*** 加鹽驗證* @param password 待驗證的密碼* @param dbPassword 數據庫中的密碼:鹽值$最終密碼* @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.加密待驗證的密碼String finalPassword = DigestUtils.md5DigestAsHex((salt+password).getBytes(StandardCharsets.UTF_8));// 3.對比if (finalPassword.equals(dbFinalPassword)) {return true;}return false;}public static void main(String[] args) {// 得到鹽值和最終密碼一共65位System.out.println(encrypt("111"));}
}
在該類中生成一個 main 方法,測試最后生成的字符串符不符合預期,輸出結果:
將上述的字符串,定義為一個字符串,驗證是否加密成功。?
public static void main(String[] args) {
/* // 得到鹽值和最終密碼一共65位System.out.println(encrypt("111"));*/String dbPassword = "0f0d62e88c6c41dc83163e2813147111$b7321a14e1e5f3360a0d0d59e2b3cd6e";System.out.println("當密碼為123時:"+decrypt("123",dbPassword));System.out.println("當密碼為111時:"+decrypt("111",dbPassword));}
?
2.2 注冊頁面中進行密碼加鹽
/*** 注冊* @param userinfo* @return*/@RequestMapping("/reg")public ResultAjax reg(Userinfo userinfo) {// 1.校驗參數if (userinfo == null || !StringUtils.hasLength(userinfo.getUsername()) ||!StringUtils.hasLength(userinfo.getPassword())) {return ResultAjax.fail(-1,"異常");}// 實現密碼加鹽userinfo.setPassword(PasswordUtils.encrypt(userinfo.getPassword()));// 2.請求接口 service 進行添加接口int ret = userService.reg(userinfo);// 3.將結果返回給前端return ResultAjax.success(ret);}
2.3 登錄頁面進行加鹽的解密
/*** 登錄* @param userinfoVO* @return*/@RequestMapping("/login")public ResultAjax login(UserinfoVO userinfoVO, HttpServletRequest request) {// 1.參數校驗if (userinfoVO == null || !StringUtils.hasLength(userinfoVO.getUsername())|| !StringUtils.hasLength(userinfoVO.getPassword())) {// 非法登錄return ResultAjax.fail(-1,"非法登錄!");}// 2.根據用戶名查詢對象,判斷用戶名是否錯誤Userinfo userinfo = userService.getUserByName(userinfoVO.getUsername());if (userinfo == null && userinfo.getId() == 0) {return ResultAjax.fail(-2,"賬號或密碼錯誤!");}// 3.使用對象中的密碼和輸入的密碼進行對比,判斷密碼是否錯誤// 加鹽解密if (!PasswordUtils.decrypt(userinfoVO.getPassword(),userinfo.getPassword())) {return ResultAjax.fail(-2,"賬號或密碼錯誤!");}// 4.成功后將對象存儲到 session 中HttpSession session = request.getSession();session.setAttribute(ApplicationVariable.SESSION_USERINFO_KEY,userinfo);// 5.結果返回給用戶return ResultAjax.success(1);}
?此時查詢數據庫中,只有一條數據,下面再注冊一個用戶,觀察是否生成密碼。
2.4 注冊和登錄
注冊頁面
?輸入用戶名 lisi 和密碼 123 后,提示注冊成功。
在數據中查詢對應的數據,驗證密碼 123 被加秘為一大串字符串。
再來到登錄頁面,輸入用戶名 lisi 和密碼 123,提示登陸成功。?
跳轉到個人頁面。
注冊頁面和登錄頁面的講解和源碼在前兩篇博客中已有詳細的講解,感興趣可以去看看。
本篇博客到這里就結束了,感謝各位的觀看。?