SpringBoot中隨機鹽值+雙重SHA256加密實戰(zhàn)
1.SHA-256和Salt
1.1.什么是SHA-256
SHA-256是一種信息摘要算法,也是一種密碼散列函數(shù)。對于任意長度的消息,SHA256都會產(chǎn)生一個256bit長的散列值(哈希值),用于確保信息傳輸完整一致,稱作消息摘要。這個摘要相當(dāng)于是個長度為32個字節(jié)的數(shù)組,通常用一個長度為64的十六進制字符串來表示。
SHA-256的具備以下幾個關(guān)鍵特點:
- 固定長度輸出:無論輸入數(shù)據(jù)的大小,SHA-256都會產(chǎn)生一個256位(32字節(jié))的固定長度散列值。
- 不可逆性:SHA-256的設(shè)計使得從生成的散列值無法還原原始輸入數(shù)據(jù)。這種不可逆性在安全性上是非常重要的。
- 抗碰撞性:找到兩個不同的輸入數(shù)據(jù)具有相同的散列值(碰撞)是極其困難的。雖然理論上碰撞可能發(fā)生,但SHA-256被設(shè)計得非常抗碰撞。
除了SHA-256之外,還有一個密碼散列函數(shù)MD5,過去也常被用于密碼加密,但MD5在安全性上低于SHA-256,現(xiàn)在已經(jīng)很少用于密碼加密了,本文不做考慮。
SHA-256 和 MD5 的比較:
特性 | SHA-256 | MD5 |
---|---|---|
輸出長度 | 256 位(64 個十六進制字符) | 128 位(32 個十六進制字符) |
安全性 | 高 | 低 |
計算速度 | 較慢 | 快 |
抗碰撞能力 | 強 | 弱 |
應(yīng)用場景 | 數(shù)據(jù)完整性校驗、數(shù)字簽名、密碼存儲、區(qū)塊鏈 | 曾用于文件校驗、密碼存儲 |
推薦使用 | 是 | 否 |
1.2.什么是隨機鹽值
鹽值(salt) 是一種在密碼學(xué)和安全計算中常用的隨機數(shù)據(jù),用于增強密碼散列的安全性。
隨機鹽值(random salt)是一種用于增強密碼散列安全性的技術(shù)。它是一個隨機生成的數(shù)據(jù)塊,在將密碼輸入散列函數(shù)之前,將鹽值與密碼組合。通過引入隨機鹽值,可以有效地防止彩虹表攻擊和相同密碼散列值重復(fù)的問題。
鹽值的作用:
- 防止彩虹表攻擊: 彩虹表是一個預(yù)計算的哈希值數(shù)據(jù)庫,用于快速查找常見密碼的哈希值。通過在密碼哈希之前加入隨機鹽值,即使密碼相同,其最終的哈希值也會不同,從而使彩虹表無效。
- 避免散列值重復(fù): 如果兩個用戶使用相同的密碼,在沒有鹽值的情況下,他們的哈希值會相同。加入鹽值后,即使密碼相同,生成的哈希值也會不同,這有助于防止攻擊者通過觀察哈希值來推測用戶是否使用了相同的密碼。
- 增加攻擊難度: 鹽值增加了密碼哈希的復(fù)雜性。即使攻擊者獲取了存儲的哈希值和鹽值,他們?nèi)孕鑼γ總€鹽值進行單獨的破解,顯著增加了破解的時間和計算成本。
1.3.如何進行加密操作
本文采用的加密方式是在前端采用md加密防止明文傳輸,后端對密碼二次加密后再進行隨機鹽值的混入。
2.前端實現(xiàn)
引入md5.min.js
<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>登錄</title> <!-- jquery --> <script type="text/javascript" th:src="@{/js/jquery.min.js}"></script> <!-- bootstrap --> <link rel="stylesheet" type="text/css" th:href="@{/bootstrap/css/bootstrap.min.css}" rel="external nofollow" /> <script type="text/javascript" th:src="@{/bootstrap/js/bootstrap.min.js}"></script> <!-- jquery-validator --> <script type="text/javascript" th:src="@{/jquery-validation/jquery.validate.min.js}"></script> <script type="text/javascript" th:src="@{/jquery-validation/localization/messages_zh.min.js}"></script> <!-- md5.js --> <script type="text/javascript" th:src="@{/js/md5.min.js}"></script> <!-- common.js --> <script type="text/javascript" th:src="@{/js/common.js}"></script> </head> <body> <form name="loginForm" id="loginForm" method="post" style="width:50%; margin:0 auto"> <h2 style="text-align:center; margin-bottom: 20px">用戶登錄</h2> <div class="form-group"> <div class="row"> <label class="form-label col-md-4">請輸入手機號碼</label> <div class="col-md-5"> <input id="mobile" minlength="11" maxlength="11" name="mobile" class="form-control" type="text" placeholder="手機號碼" required="true"/> </div> </div> </div> <div class="form-group"> <div class="row"> <label class="form-label col-md-4">請輸入密碼</label> <div class="col-md-5"> <input id="password" name="password" class="form-control" type="password" placeholder="密碼" required="true" /> </div> </div> </div> <div class="row"> <div class="col-md-5"> <button class="btn btn-primary btn-block" type="reset" onclick="reset()">重置</button> </div> <div class="col-md-5"> <button class="btn btn-primary btn-block" type="submit" onclick="login()">登錄</button> </div> </div> </form> </body> <script> function login() { $("#loginForm").validate({ submitHandler: function (form) { doLogin(); } }); } function doLogin() { var inputPass = $("#password").val(); var salt = "1a2b3c4d"; var str = "" + salt.charAt(0) + salt.charAt(2) + inputPass + salt.charAt(5) + salt.charAt(4); var password = md5(str); $.ajax({ url: "/login/doLogin", type: "POST", data: { mobile: $("#mobile").val(), password: password }, success: function (data) { layer.closeAll(); if (data.code == 200) { layer.msg("成功"); console.log(data); document.cookie = "userTicket=" + data.object; window.location.href = "/goods/toList"; } else { layer.msg(data.message); } }, error: function () { layer.closeAll(); } }); } </script> </html>
3.后端實現(xiàn)
3.1.導(dǎo)入Maven依賴
<dependency> <groupId>commons-codec</groupId> <artifactId>commons-codec</artifactId> <version>1.15</version> </dependency> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> <version>3.12.0</version> </dependency>
3.2.密碼加密
3.2.1.密碼加鹽
首先使用Apache的RandomStringUtils
工具類,生成16位的鹽值。然后將鹽拼接到明文后面,進行SHA256加密。
這個加密后的SHA256是個固定64長度的字符串。
// 生成一個16位的隨機數(shù),也就是鹽 String salt = RandomStringUtils.randomAlphanumeric(16); // 將鹽拼接到明文后,并生成新的sha256碼 String sha256Hex = DigestUtils.sha256Hex(password + salt);
3.2.2.隨機鹽值混合
加鹽后的SHA256碼長度為80位,這里我們采用的鹽值混合規(guī)則:將SHA-256散列值的每四個字符中間插入一個鹽值字符,依次交替排列。
// 將鹽混到新生成的SHA-256碼中,之所以這樣做是為了后期解密,校驗密碼 StringBuilder sb = new StringBuilder(80); // SHA-256是64個字符,加16個字符的鹽,總共80個字符 for (int i = 0; i < 16; i++) { sb.append(sha256Hex.charAt(i * 4)); sb.append(salt.charAt(i)); sb.append(sha256Hex.charAt(i * 4 + 1)); sb.append(sha256Hex.charAt(i * 4 + 2)); sb.append(sha256Hex.charAt(i * 4 + 3)); } return sb.toString();
這樣就完成了加密的操作:密碼加鹽 + 鹽值混合。
3.3.密碼解密
3.3.1.提取鹽值和加鹽密碼
按照加密時采用的規(guī)則:將SHA-256散列值的每四個字符中間插入一個鹽值字符,依次交替排列。
我們可以將鹽值和加鹽后的SHA-256碼
// 提取鹽值和加鹽后的SHA-256碼 StringBuilder sb1 = new StringBuilder(64); StringBuilder sb2 = new StringBuilder(16); for (int i = 0; i < 16; i++) { sb1.append(encrypted.charAt(i * 5)); sb1.append(encrypted.charAt(i * 5 + 2)); sb1.append(encrypted.charAt(i * 5 + 3)); sb1.append(encrypted.charAt(i * 5 + 4)); sb2.append(encrypted.charAt(i * 5 + 1)); } String sha256Hex = sb1.toString(); String salt = sb2.toString();
3.3.2.比較密碼
最后,將取出的鹽值與原始密碼再次加鹽,再次得到加鹽密碼,與sha256Hex比較即可判斷密碼是否相同。
// 比較二者是否相同 return DigestUtils.sha256Hex(password + salt).equals(sha256Hex);
3.4.完整工具類
public class SHA256Util { /** * 加密 * 生成鹽和加鹽后的SHA-256碼,并將鹽混入到SHA-256碼中,對SHA-256密碼進行加強 **/ public static String encryptPassword(String password) { // 生成一個16位的隨機數(shù),也就是鹽 String salt = RandomStringUtils.randomAlphanumeric(16); // 將鹽拼接到明文后,并生成新的sha256碼 String sha256Hex = DigestUtils.sha256Hex(password + salt); // 將鹽混到新生成的SHA-256碼中,之所以這樣做是為了后期解密,校驗密碼 StringBuilder sb = new StringBuilder(80); // SHA-256是64個字符,加16個字符的鹽,總共80個字符 for (int i = 0; i < 16; i++) { sb.append(sha256Hex.charAt(i * 4)); sb.append(salt.charAt(i)); sb.append(sha256Hex.charAt(i * 4 + 1)); sb.append(sha256Hex.charAt(i * 4 + 2)); sb.append(sha256Hex.charAt(i * 4 + 3)); } return sb.toString(); } /** * 解密 * 從混入鹽的SHA-256碼中提取鹽值和加鹽后的SHA-256碼 **/ public static boolean verifyPassword(String password, String encrypted) { // 提取鹽值和加鹽后的SHA-256碼 StringBuilder sb1 = new StringBuilder(64); StringBuilder sb2 = new StringBuilder(16); for (int i = 0; i < 16; i++) { sb1.append(encrypted.charAt(i * 5)); sb1.append(encrypted.charAt(i * 5 + 2)); sb1.append(encrypted.charAt(i * 5 + 3)); sb1.append(encrypted.charAt(i * 5 + 4)); sb2.append(encrypted.charAt(i * 5 + 1)); } String sha256Hex = sb1.toString(); String salt = sb2.toString(); // 比較二者是否相同 return DigestUtils.sha256Hex(password + salt).equals(sha256Hex); } }
到此這篇關(guān)于SpringBoot中隨機鹽值+雙重SHA256加密實戰(zhàn)的文章就介紹到這了,更多相關(guān)SpringBoot 隨機鹽值+雙重SHA256加密內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Spring整合WebSocket應(yīng)用示例(上)
以下教程是小編在參與開發(fā)公司的一個crm系統(tǒng),整理些相關(guān)資料,在該系統(tǒng)中有很多消息推送功能,在其中用到了websocket技術(shù)。下面小編整理分享到腳本之家平臺供大家參考2016-04-04Spring?Cloud?Ribbon?負(fù)載均衡使用策略示例詳解
Spring?Cloud?Ribbon?是基于Netflix?Ribbon?實現(xiàn)的一套客戶端負(fù)載均衡工具,Ribbon客戶端組件提供了一系列的完善的配置,如超時,重試等,這篇文章主要介紹了Spring?Cloud?Ribbon?負(fù)載均衡使用策略示例詳解,需要的朋友可以參考下2023-03-03SpringBoot預(yù)加載與懶加載實現(xiàn)方法超詳細講解
Spring一直被詬病啟動時間慢,可Spring/SpringBoot是輕量級的框架。因為當(dāng)Spring項目越來越大的時候,在啟動時加載和初始化Bean就會變得越來越慢,很多時候我們在啟動時并不需要加載全部的Bean,在調(diào)用時再加載就行,那這就需要預(yù)加載與懶加載的功能了2022-11-11深入探究Java中的HashMap為什么會產(chǎn)生死循環(huán)
HashMap?死循環(huán)發(fā)生在?JDK?1.8?之前的版本中,這篇文章主要來和大家深入探究一下為什么Java中HashMap會產(chǎn)生死循環(huán),感興趣的小伙伴可以了解一下2023-05-05IntelliJ IDEA : .java文件左下角顯示"J"圖標(biāo)的問題
IntelliJ IDEA : .java文件 左下角顯示“J”,并且不能執(zhí)行代碼,本文通過圖文并茂的形式給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友參考下吧2020-10-10JAVA根據(jù)ip地址獲取歸屬地的實現(xiàn)方法
本文主要介紹了JAVA根據(jù)ip地址獲取歸屬地的實現(xiàn)方法,要通過Java程序獲取IP地址對應(yīng)的城市,需要借助第三方的IP地址庫,下面就來介紹一下,感興趣的可以了解一下2023-10-10