目錄
前言
一、手寫加鹽算法
1.1、加密
1.1.1、加密思路
1.1.2、加密簡圖
1.1.3、代碼實(shí)現(xiàn)
1.2、解密
1.2.1、解密思路
1.2.2、解密代碼
1.3、驗(yàn)證
二、使用?Spring Security 框架實(shí)現(xiàn)加鹽算法
前言
為什么要使用加鹽的方式對密碼進(jìn)行加密?我們知道傳統(tǒng)的 md5 加密方式是可以通過 “彩虹表” 很容易破解的,因?yàn)?md5 加密每次生成的密碼都是固定的~ 為了解決這個(gè)問題,就出現(xiàn)了加鹽加密方式,每次生成的密碼都是不一樣的~?
接下來我會(huì)講兩種加鹽加密方式(手寫加鹽算法、使用 Spring Security 框架),那么就一起來看一下使用加鹽的方式進(jìn)行加密和解密吧~
一、手寫加鹽算法
實(shí)際上就是一個(gè)加密解密的過程~
1.1、加密
1.1.1、加密思路
加密的主要有以下幾步:
- 產(chǎn)生隨機(jī)鹽值(32位)。解釋:這里可以使用 UUID 中的?randomUUID 方法生成一個(gè)長度為 36 位的隨機(jī)鹽值(格式為xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx (8-4-4-4-12)),而我們約定的格式中(完全由自己定義)不需要"-",因此將產(chǎn)生的隨機(jī)鹽值先使用 toString 方法轉(zhuǎn)化成字符串,最后用?replaceAll 方法將字符串中的 "-" 用空字符串代替即可。
- 將鹽值和明文拼接起來,然后整體使用 md5 加密,得到密文(32位)。解釋:這里就是對明文進(jìn)行加密的過程。
- 根據(jù)自己約定的格式,使用 “32位鹽值?+&+ 32位加鹽后得到的密文” 方式進(jìn)行加密得到最終密碼。解釋:我們這樣去約定格式,是為了更容易的得到鹽值(通過 split 以 $ 為分割符進(jìn)行分割,得到的字符串?dāng)?shù)組下標(biāo) 0 的就是我們需要的鹽值),讓我們能夠解密。
Ps:UUID的第一個(gè)部分與時(shí)間有關(guān),如果你在生成一個(gè)UUID之后,過幾秒又生成一個(gè)UUID,則第一個(gè)部分不同,其余相同。UUID的唯一缺陷在于生成的結(jié)果串會(huì)比較長。
1.1.2、加密簡圖
1.1.3、代碼實(shí)現(xiàn)
/**
* 加鹽并生成密碼
* @param password 用戶輸入的明文密碼
* @return 保存到數(shù)據(jù)庫中的密碼
*/
public static String encrypt(String password) {
// 產(chǎn)生鹽值(32位) 這里要注意去掉 UUID 生成 -
String salt = UUID.randomUUID().toString().replaceAll("-", "");
// 生成加鹽之后的密碼((鹽值 + 密碼)整體 md5 加密)
String saltPassword = DigestUtils.md5DigestAsHex((salt + password).getBytes());
// 生成最終密碼(保存到數(shù)據(jù)庫中的密碼)[自己約定的格式:32位鹽值 +&+ 32位加鹽后的密碼]
// 這樣約定格式是為了解密的時(shí)候方便得到鹽值
String finalPassword = salt + "$" + saltPassword;
return finalPassword;
}
1.2、解密
1.2.1、解密思路
解密的一個(gè)大體思路如下:
- 用戶輸入的密碼使用剛剛講到的加密思路再進(jìn)行加密一次。解釋:這里我們重載上面講到的加密方法,需要傳入兩個(gè)參數(shù),分別是用戶輸入的密碼和鹽值(這里的鹽值我們可以通過對數(shù)據(jù)庫中的最終加密密碼進(jìn)行字符串分割 $ 符號(hào)得到),然后對用戶信息進(jìn)行加密。
- 將剛剛加密的密碼 對比 數(shù)據(jù)庫保存的用戶加密的最終密碼 是否一致即可。
通過上述思路可以知道,實(shí)習(xí)解密思路需要兩個(gè)方法~
1.2.2、解密代碼
/**
* 加鹽生成密碼(方法1的重載)
* 此方法在驗(yàn)證密碼的適合需要(將用戶輸入的密碼使用同樣的鹽值加密后對比)
* @param password 明文
* @param salt 固定的鹽值
* @return 最終密碼
*/
public static String encrypt(String password, String salt) {
// 生成加鹽后的密碼
String saltPassword = DigestUtils.md5DigestAsHex((salt + password).getBytes());
// 生成最終密碼(約定格式: 32位 鹽值 +&+ 32位加鹽后的密碼)
String finalPassword = salt + "$" + saltPassword;
return finalPassword;
}
/**
* 驗(yàn)證密碼
* @param inputPassword 用戶輸入明文密碼
* @param finalPassword 數(shù)據(jù)庫中保存的最終密碼
* @return
*/
public static boolean check(String inputPassword, String finalPassword) {
// 判空處理
if(StringUtils.hasLength(inputPassword) && StringUtils.hasLength(finalPassword) &&
finalPassword.length() == 65) {
// 得到鹽值(之前約定: $前面的就是鹽值)
String salt = finalPassword.split("\\$")[0];// 由于 $ 在這里也可以表示通配符,所以需要使用 \\ 進(jìn)行轉(zhuǎn)義
// 使用之前的加密步驟將明文進(jìn)行加密,生成最終密碼
String confirmPassword = PasswordUtils.encrypt(inputPassword, salt);
// 對比兩個(gè)最終密碼是否相同
return confirmPassword.equals(finalPassword);
}
return false;
}
1.3、驗(yàn)證
我們可以先將上述方法都封裝在一個(gè)類中,測試的時(shí)候直接調(diào)用此類即可。?
代碼如下:
import org.springframework.util.DigestUtils;
import org.springframework.util.StringUtils;
import java.util.UUID;
public class PasswordUtils {
/**
* 加鹽并生成密碼
* @param password 用戶輸入的明文密碼
* @return 保存到數(shù)據(jù)庫中的密碼
*/
public static String encrypt(String password) {
// 產(chǎn)生鹽值(32位) 這里要注意去掉 UUID 生成 -
String salt = UUID.randomUUID().toString().replaceAll("-", "");
// 生成加鹽之后的密碼((鹽值 + 密碼)整體 md5 加密)
String saltPassword = DigestUtils.md5DigestAsHex((salt + password).getBytes());
// 生成最終密碼(保存到數(shù)據(jù)庫中的密碼)[自己約定的格式:32位鹽值 +&+ 32位加鹽后的密碼]
// 這樣約定格式是為了解密的時(shí)候方便得到鹽值
String finalPassword = salt + "$" + saltPassword;
return finalPassword;
}
/**
* 加鹽生成密碼(方法1的重載)
* 此方法在驗(yàn)證密碼的適合需要(將用戶輸入的密碼使用同樣的鹽值加密后對比)
* @param password 明文
* @param salt 固定的鹽值
* @return 最終密碼
*/
public static String encrypt(String password, String salt) {
// 生成加鹽后的密碼
String saltPassword = DigestUtils.md5DigestAsHex((salt + password).getBytes());
// 生成最終密碼(約定格式: 32位 鹽值 +&+ 32位加鹽后的密碼)
String finalPassword = salt + "$" + saltPassword;
return finalPassword;
}
/**
* 驗(yàn)證密碼
* @param inputPassword 用戶輸入明文密碼
* @param finalPassword 數(shù)據(jù)庫中保存的最終密碼
* @return
*/
public static boolean check(String inputPassword, String finalPassword) {
// 判空處理
if(StringUtils.hasLength(inputPassword) && StringUtils.hasLength(finalPassword) &&
finalPassword.length() == 65) {
// 得到鹽值(之前約定: $前面的就是鹽值)
String salt = finalPassword.split("\\$")[0];// 由于 $ 在這里也可以表示通配符,所以需要使用 \\ 進(jìn)行轉(zhuǎn)義
// 使用之前的加密步驟將明文進(jìn)行加密,生成最終密碼
String confirmPassword = PasswordUtils.encrypt(inputPassword, salt);
// 對比兩個(gè)最終密碼是否相同
return confirmPassword.equals(finalPassword);
}
return false;
}
}
首先我們可以模擬用戶注冊,對密碼進(jìn)行連續(xù)的三次加密,觀察最后結(jié)果,是否每次產(chǎn)生的密碼都不一致~ 最后模擬用戶登錄,分別輸入正確的密碼和錯(cuò)誤的密碼,觀察運(yùn)行結(jié)果~
代碼如下:
public static void main(String[] args) {
//用戶注冊時(shí)的密碼
String password = "1234";
//分別進(jìn)行三次解密,觀察結(jié)果
String finalPassword = PasswordUtils.encrypt(password);
System.out.println(finalPassword);
System.out.println(PasswordUtils.encrypt(password));
System.out.println(PasswordUtils.encrypt(password));
//驗(yàn)證密碼(用戶登錄)
String input1 = "1234";//正確密碼
String input2= "12345";//錯(cuò)誤密碼
System.out.println("密碼1為正確密碼:" + PasswordUtils.check(input1, finalPassword));
System.out.println("密碼2為錯(cuò)誤密碼:" + PasswordUtils.check(input2, finalPassword));
}
運(yùn)行結(jié)果如下:
二、使用?Spring Security 框架實(shí)現(xiàn)加鹽算法
首先需要引入 Spring Security 依賴
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
具體實(shí)現(xiàn):
實(shí)際上我們剛剛手寫的加鹽算法就是在模仿著寫 Spring Security 的加鹽算法的底層,因此使用上也差不多,直接上代碼~
Ps:值得注意的是,如果你只需要使用該框架中的加密方法,那么需要在文件的啟動(dòng)類?DemoApplication 中排除?Spring Security 框架的自檢測機(jī)制(此框架有自己的登錄驗(yàn)證機(jī)制,不關(guān)閉的話會(huì)多出一個(gè)登錄頁面)代碼如下:
@SpringBootApplication(exclude = {SecurityAutoConfiguration.class})//排除 Spring Security 自動(dòng)加載
代碼如下:文章來源:http://www.zghlxwxcb.cn/news/detail-594957.html
/**
* 使用 spring Security 實(shí)現(xiàn)加鹽
* @param args
*/
public static void main(String[] args) {
//加鹽的工具類
BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
//用戶注冊時(shí)的密碼
String password = "1234";
//加密(連續(xù)加密三次觀察結(jié)果)
String finalPassword = passwordEncoder.encode(password);
System.out.println(finalPassword);
System.out.println(passwordEncoder.encode(password));
System.out.println(passwordEncoder.encode(password));
//模擬用戶登錄
String s1 = "1234";//正確密碼
String s2 = "12345";//錯(cuò)誤密碼
System.out.println("輸入正確的密碼:" + passwordEncoder.matches(s1, finalPassword));
System.out.println("輸入錯(cuò)誤的密碼:" + passwordEncoder.matches(s2, finalPassword));
}
文章來源地址http://www.zghlxwxcb.cn/news/detail-594957.html
到了這里,關(guān)于密碼如何“加鹽加密”處理?程序員一定要掌握的知識(shí)的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!