Java雙重MD5加密實(shí)現(xiàn)安全登錄
一:?jiǎn)栴}引入
今天看到一篇文章說(shuō)使用MD5對(duì)密碼進(jìn)行加密存儲(chǔ)也還不能做到很安全,網(wǎng)上有在線解密MD5的網(wǎng)站,我一搜,還真有。接下來(lái)我嘗試對(duì)我存儲(chǔ)在數(shù)據(jù)庫(kù)中的密碼進(jìn)行解密操作:
可以看到成功將我的密碼解密出來(lái),這讓我很吃驚,因?yàn)槲覀兌贾繫D5算法是不可逆的,因?yàn)樗瞧涫且环N散列函數(shù),使用的是hash算法,在計(jì)算過(guò)程中原文的部分信息是丟失了的。那么為什么網(wǎng)站中可以將我的密碼解密出來(lái)呢?
經(jīng)過(guò)一番查找后發(fā)現(xiàn),原來(lái)在線解密工具的解密原理很簡(jiǎn)單,其原理是收集用戶常用的簡(jiǎn)單密碼形成了一個(gè)密碼字典,并將字典中的密碼用MD5加密后存儲(chǔ)起來(lái),在所謂的“解密“的時(shí)候,就將真正用戶密碼加密都的密文與已存儲(chǔ)的密碼相比較,如該密文存在于字典當(dāng)中,即可以“解密”。因此,簡(jiǎn)單的用MD5對(duì)用戶密碼加密還不安全,我們?cè)O(shè)置密碼時(shí)候也通常都會(huì)有復(fù)雜度檢測(cè),比如密碼必須帶英文數(shù)字什么的,就是為了安全性考慮。
知道其解密原理之后,我們嘗試一下復(fù)雜點(diǎn)的密碼看看能不能“解密”出來(lái),首先對(duì)密碼“qweasd666”進(jìn)行加密:
加密之后我們選取32位小寫(xiě)的密文進(jìn)行解密操作:
可以看到解密失敗,說(shuō)明其原理就是我們上面所說(shuō)的收集常用的密文進(jìn)行一一對(duì)應(yīng)。既然解密失敗了,那么說(shuō)明“qweasd666”這個(gè)密碼是安全的,你們可以都設(shè)置這個(gè)密碼(doge)。
言歸正傳,說(shuō)到這個(gè)網(wǎng)站成功將我精心設(shè)置的密碼給破解了,這讓我很沒(méi)有安全感,而且我感覺(jué)我很沒(méi)有面子,我一定要將我的登錄安全等級(jí)進(jìn)行提升。
除了上面提到的解密操作之外,還有一個(gè)很大的問(wèn)題就是在前端將數(shù)據(jù)傳輸過(guò)來(lái)時(shí)候http采用的是明文傳輸,如果傳輸數(shù)據(jù)包被截取,那么就算你后端的加密算法有多復(fù)雜,你的密碼也會(huì)被別人知道。
二:解決方案
2.1:第一次加密
找到問(wèn)題所在之后,我們就可以對(duì)癥下藥了,首先我覺(jué)得要解決的是http明文傳輸問(wèn)題,因?yàn)檫@個(gè)風(fēng)險(xiǎn)最大,普通抓包就能抓取密碼,這不是很恐怖的一件事嗎。解決方案也很簡(jiǎn)單,既然明文傳輸不安全,那么我們加密后再進(jìn)行傳輸不就行了嗎?
我選用的方案仍然還是MD5加密,但是對(duì)密碼加密前還要加入一個(gè)固定salt。salt?是加鹽嗎,其實(shí)差不多,更不如說(shuō)是加點(diǎn)“佐料”。其基本想法是這樣的:當(dāng)用戶首次提供密碼時(shí)(通常是注冊(cè)時(shí)),由程序往這個(gè)密碼里撒一些“佐料”,為了減輕開(kāi)發(fā)壓力,這個(gè)佐料對(duì)于每一個(gè)用戶都是相同的,然后再散列。這樣就能防止傳輸過(guò)程中出現(xiàn)明文密碼泄露。
2.2:第二次加密
注意上面我提到的是防止出現(xiàn)明文密碼泄漏,但是這并不代表密文不會(huì)泄漏,假如黑客截取你通過(guò)http傳輸?shù)慕?jīng)過(guò)加密后的密碼,或者數(shù)據(jù)庫(kù)發(fā)生泄漏導(dǎo)致加密后的密碼被黑客盜取,黑客通過(guò)解析前端js文件獲得前端固定鹽值,那么黑客可能可以通過(guò)彩虹表進(jìn)行反向查詢得到原始密碼。這時(shí)候就二次加密的重要性就出來(lái)了,二次加密實(shí)現(xiàn)原理為對(duì)前端傳過(guò)來(lái)經(jīng)過(guò)加密之后的密碼再次和一個(gè)隨機(jī)Salt值結(jié)合后進(jìn)行加密(注意這次是隨機(jī)的),鹽值會(huì)在用戶登陸的時(shí)候隨機(jī)生成,并存在數(shù)據(jù)庫(kù)中。
這時(shí)候可能你會(huì)和我剛開(kāi)始一樣也會(huì)有一個(gè)疑惑,那就是你把隨機(jī)鹽值保存到數(shù)據(jù)庫(kù)中了,那么如果數(shù)據(jù)庫(kù)泄漏那你的隨機(jī)鹽值還有什么作用呢?黑客拿到加密數(shù)據(jù)進(jìn)行解密后去除鹽值不是就能得到密碼了嗎?是的,黑客有可能通過(guò)這樣的手段得到密碼,但是前提是他能解密出來(lái),要知道的是MD5是無(wú)法直接破解的,只能通過(guò)窮舉法進(jìn)行解密。就算黑客拿到了數(shù)據(jù)庫(kù)中的加密密碼,但是不知道后端的加密過(guò)程,他就無(wú)法進(jìn)行解析,就算黑客同時(shí)知道加密過(guò)程,由于經(jīng)過(guò)了二次加鹽二次加密,這時(shí)候的密文是很難很難被解析出來(lái)的,隨著加密數(shù)據(jù)的復(fù)雜度增加,破解成本是呈指數(shù)級(jí)增加的,在那么大的成本面前相信沒(méi)有什么黑客愿意去嘗試。當(dāng)然,salt也不一定要加在最前面或最后面,也可以插在中間,也可以分開(kāi)插入,也可以倒序,程序設(shè)計(jì)時(shí)可以靈活調(diào)整,都可以使破解的難度呈指數(shù)型增長(zhǎng)。
2.3:具體實(shí)現(xiàn)
2.3.1:用戶注冊(cè)
- 前端對(duì)用戶輸入的密碼進(jìn)行md5加密(固定的salt)
- 將加密后的密碼傳遞到后端
- 后端隨機(jī)生成一個(gè)salt
- 使用生成salt對(duì)前端傳過(guò)來(lái)的密碼進(jìn)行加密,然后將加密后密碼和salt一起保存到db中
2.3.2:用戶登錄
- 前端對(duì)用戶輸入的密碼進(jìn)行md5加密(固定的salt)
- 將加密后的密碼傳遞到后端
- 后端使用用戶賬號(hào)取出用戶信息
- 后端對(duì)加密后的密碼在進(jìn)行md5加密(取出鹽),然后與數(shù)據(jù)庫(kù)中存儲(chǔ)的密碼進(jìn)行對(duì)比
- 匹配則登錄成功,否則登錄失敗
三:代碼實(shí)現(xiàn)
3.1:第一次加密
3.1.1:前端
const params = { ...this.ruleForm, sex: this.ruleForm.sex === '女' ? '0' : '1', //設(shè)置密碼加密(加上固定salt值) password: md5(this.ruleForm.password + this.salt) } addEmployee(params).then(res => { if (res.code === 1) { this.$message.success('員工添加成功!') if (!st) { this.goBack() } else { this.ruleForm = { username: '', 'name': '', 'phone': '', password: '', // 'rePassword': '',/ 'sex': '男', 'idNumber': '' } } } else { this.$message.error(res.msg || '操作失敗') } }
3.1.2:后端
/** * 添加員工 */ @PostMapping public R<String> save(@RequestBody Employee employee){ //生成隨機(jī)salt值 String salt = RandomStringUtils.randomAlphanumeric(5); //設(shè)置隨機(jī)鹽值 employee.setSalt(salt); //設(shè)置密碼二次加密 employee.setPassword(DigestUtils.md5DigestAsHex((salt + employee.getPassword()).getBytes())); boolean save = employeeService.save(employee); if(save){ return R.success("添加成功!"); } return R.error("添加失敗!"); }
3.2:第二次加密
3.2.1:前端
const params = { ...this.loginForm, //登錄密碼加上固定鹽值后發(fā)送 password: md5(this.loginForm.password + this.salt), } let res = await loginApi(params) if (String(res.code) === '1') { localStorage.setItem('userInfo', JSON.stringify(res.data)) window.location.href = '/backend/index.html' } else { this.$message.error(res.msg) this.loading = false }
3.2.2:后端
/** * 員工登錄 */ @PostMapping("/login") public R<Employee> login(HttpServletRequest request, @RequestBody Employee employee){ //1.獲取傳輸過(guò)來(lái)的加密后的密碼值 String encryPassword = employee.getPassword(); //2.從數(shù)據(jù)庫(kù)中獲取用戶信息 LambdaQueryWrapper<Employee> lqw = new LambdaQueryWrapper<>(); lqw.eq(Employee::getUsername,employee.getUsername()); Employee emp = employeeService.getOne(lqw); //3.如果沒(méi)有查詢到則返回登陸失敗查詢結(jié)果 if(emp == null){ return R.error("沒(méi)有查詢到該用戶信息!"); } //4.獲取注冊(cè)時(shí)保存的隨機(jī)鹽值 String salt = emp.getSalt(); //5.將頁(yè)面提交的密碼password進(jìn)行md5二次加密 String password = DigestUtils.md5DigestAsHex((salt + encryPassword).getBytes()); //6.密碼比對(duì),如果不一致則返回登陸失敗結(jié)果 if(!emp.getPassword().equals(password)){ return R.error("密碼錯(cuò)誤!"); } //7.查看員工狀態(tài)是否可用 if(emp.getStatus() == 0){ return R.error("該員工已被禁用!"); } //8.登錄成功,將員工id存入Session對(duì)象并返回登錄成功結(jié)果 request.getSession().setAttribute("employee",emp.getId()); return R.success(emp); }
到此這篇關(guān)于Java雙重MD5加密實(shí)現(xiàn)安全登錄的文章就介紹到這了,更多相關(guān)Java雙重MD5加密 內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java中基于Nacos實(shí)現(xiàn)Sentinel規(guī)則持久化詳解
這篇文章主要介紹了Java中基于Nacos實(shí)現(xiàn)Sentinel規(guī)則持久化詳解,Sentinel Dashboard中添加的規(guī)則數(shù)據(jù)存儲(chǔ)在內(nèi)存,微服務(wù)停掉規(guī)則數(shù)據(jù)就消失,在?產(chǎn)環(huán)境下不合適,我們可以將Sentinel規(guī)則數(shù)據(jù)持久化到Nacos配置中?,讓微服務(wù)從Nacos獲取規(guī)則數(shù)據(jù),需要的朋友可以參考下2023-09-09一小時(shí)迅速入門(mén)Mybatis之Prepared Statement與符號(hào)的使用
這篇文章主要介紹了一小時(shí)迅速入門(mén)Mybatis之Prepared Statement與符號(hào)的使用,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-09-09Java通過(guò)jersey實(shí)現(xiàn)客戶端圖片上傳示例
本篇文章主要介紹了Java通過(guò)jersey實(shí)現(xiàn)客戶端圖片上傳示例,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下。2017-03-03Java Socket編程實(shí)現(xiàn)簡(jiǎn)單的問(wèn)候服務(wù)
這篇文章主要為大家介紹了Java Socket編程實(shí)現(xiàn)簡(jiǎn)單的問(wèn)候服務(wù),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-01-01Spring Boot項(xiàng)目實(shí)戰(zhàn)之?dāng)r截器與過(guò)濾器
這篇文章主要介紹了Spring Boot項(xiàng)目實(shí)戰(zhàn)之?dāng)r截器與過(guò)濾器,文中給大家詳細(xì)介紹了springboot 攔截器和過(guò)濾器的基本概念,過(guò)濾器的配置,需要的朋友可以參考下2018-01-01Java中使用輾轉(zhuǎn)相除法求最大公約數(shù)
這篇文章主要介紹了Java中使用輾轉(zhuǎn)相除法求最大公約數(shù),本文直接給出代碼實(shí)例,需要的朋友可以參考下2015-05-05HashSet工作原理_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理
HashSet 底層采用 HashMap 來(lái)保存所有元素,因此 HashSet 的實(shí)現(xiàn)比較簡(jiǎn)單。接下來(lái)通過(guò)本文給大家介紹HashSet工作原理_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理,需要的朋友可以參考下2017-04-04Java如何使用while循環(huán)計(jì)算一個(gè)整數(shù)的位數(shù)
這篇文章主要介紹了Java使用while循環(huán)計(jì)算一個(gè)整數(shù)的位數(shù)方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-01-01