一、salt和MD5
简单认识
加盐(盐英文就是salt):在密码学中,是指通过在密码任意固定位置插入特定的字符串,让散列后的结果和使用原始密码的散列结果不相符,这种过程称之为”加盐“。
MD5信息摘要算法(英语:MD5 Message-Digest Algorithm):一种被广泛使用的密码散列函数,可以产生出一个128位(16字节)的散列值(hash value),用于确保信息传输完整一致。MD5由美国密码学家罗纳德·李维斯特(Ronald Linn Rivest)设计,于1992年公开,用以取代MD4算法。这套算法的程序在 RFC 1321 标准中被加以规范。1996年后该算法被证实存在弱点,可以被加以破解,对于需要高度安全性的数据,专家一般建议改用其他算法,如SHA-2。2004年,证实MD5算法无法防止碰撞(collision),因此不适用于安全性认证,如SSL公开密钥认证或是数字签名等用途。
MD5原理:MD5码以512位分组来处理输入的信息,且每一分组又被划分为16个32位子分组,经过了一系列的处理后,算法的输出由四个32位分组组成,将这四个32位分组级联后将生成一个128位散列值。
说到这里,细心的朋友可以发现,你这不是说MD5可以被破解,存在弱点,现在的MD5破解工具也很多,你为什么还在使用呢。其实作为初学者,我们是一步步接触更好的技术,我也是在接触好技术的过程,对项目加密等问题也在不断完善,这是我学习过程使用到的加密技术,所以在此记录分享一下。并且随机盐与MD5融合破解难度大大增加,很难。
如何使用
两次MD5加密
- 用户端:PASS = MD5(明文,就是用户输入的密码;明文+固定Salt)
- 第一次MD5加密:前端传给后端时候,进行第一次MD5加密,防止密码在网络中以明文传输。
- 服务端:PASS = MD5(用户端PASS,就是前端加密后的密码;前端PASS+随机Salt)
- 第二次MD5加密:服务器端(后端)接收到MD5加密后的密码存入数据库之前进行第二次MD5加密。
- 这次加密采用的是随机生成盐值,然后与密码拼接,进行MD5加密,再将随机生成的盐值混合(按照自己设置的规律)到加密后的密码中存入数据库,这样就不会存在盐值随机造成的自己无法破解。
二、具体实现
前端
引入MD5的js文件
md5.min.js:直接给大家,节省大家寻找的时间,文件取名相同,复制粘贴即可。
1 2 3 4 5 6 7 | /** * [js-md5]{@link https://github.com/emn178/js-md5} * * @namespace md5 * @version 0.7.2 */ ! function (){ "use strict" ; function Md5(t){ if (t)blocks[0]=blocks[16]=blocks[1]=blocks[2]=blocks[3]=blocks[4]=blocks[5]=blocks[6]=blocks[7]=blocks[8]=blocks[9]=blocks[10]=blocks[11]=blocks[12]=blocks[13]=blocks[14]=blocks[15]=0, this .blocks=blocks, this .buffer8=buffer8; else if (ARRAY_BUFFER){ var r= new ArrayBuffer(68); this .buffer8= new Uint8Array(r), this .blocks= new Uint32Array(r)} else this .blocks=[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]; this .h0= this .h1= this .h2= this .h3= this .start= this .bytes= this .hBytes=0, this .finalized= this .hashed=!1, this .first=!0} var ERROR= "input is invalid type" ,WINDOW= "object" == typeof window,root=WINDOW?window:{};root.JS_MD5_NO_WINDOW&&(WINDOW=!1); var WEB_WORKER=!WINDOW&& "object" == typeof self,NODE_JS=!root.JS_MD5_NO_NODE_JS&& "object" == typeof process&&process.versions&&process.versions.node;NODE_JS?root=global:WEB_WORKER&&(root=self); var COMMON_JS=!root.JS_MD5_NO_COMMON_JS&& "object" == typeof module&&module.exports,AMD= "function" == typeof define&&define.amd,ARRAY_BUFFER=!root.JS_MD5_NO_ARRAY_BUFFER&& "undefined" != typeof ArrayBuffer,HEX_CHARS= "0123456789abcdef" .split( "" ),EXTRA=[128,32768,8388608,-2147483648],SHIFT=[0,8,16,24],OUTPUT_TYPES=[ "hex" , "array" , "digest" , "buffer" , "arrayBuffer" , "base64" ],BASE64_ENCODE_CHAR= "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" .split( "" ),blocks=[],buffer8; if (ARRAY_BUFFER){ var buffer= new ArrayBuffer(68);buffer8= new Uint8Array(buffer),blocks= new Uint32Array(buffer)}(root.JS_MD5_NO_NODE_JS||!Array.isArray)&&(Array.isArray= function (t){ return "[object Array]" ===Object.prototype.toString.call(t)}),!ARRAY_BUFFER||!root.JS_MD5_NO_ARRAY_BUFFER_IS_VIEW&&ArrayBuffer.isView||(ArrayBuffer.isView= function (t){ return "object" == typeof t&&t.buffer&&t.buffer.constructor===ArrayBuffer}); var createOutputMethod= function (t){ return function (r){ return new Md5(!0).update(r)[t]()}},createMethod= function (){ var t=createOutputMethod( "hex" );NODE_JS&&(t=nodeWrap(t)),t.create= function (){ return new Md5},t.update= function (r){ return t.create().update(r)}; for ( var r=0;ro;){ if ( this .hashed&&( this .hashed=!1,f[0]=f[16],f[16]=f[1]=f[2]=f[3]=f[4]=f[5]=f[6]=f[7]=f[8]=f[9]=f[10]=f[11]=f[12]=f[13]=f[14]=f[15]=0),r) if (ARRAY_BUFFER) for (i= this .start;h>o&&64>i;++o)a[i++]=t[o]; else for (i= this .start;h>o&&64>i;++o)f[i>>2]|=t[o]o&&64>i;++o)s=t.charCodeAt(o),128>s?a[i++]=s:2048>s?(a[i++]=192|s>>6,a[i++]=128|63&s):55296>s||s>=57344?(a[i++]=224|s>>12,a[i++]=128|s>>6&63,a[i++]=128|63&s):(s=65536+((1023&s)>18,a[i++]=128|s>>12&63,a[i++]=128|s>>6&63,a[i++]=128|63&s); else for (i= this .start;h>o&&64>i;++o)s=t.charCodeAt(o),128>s?f[i>>2]|=ss?(f[i>>2]|=(192|s>>6)>2]|=(128|63&s)s||s>=57344?(f[i>>2]|=(224|s>>12)>2]|=(128|s>>6&63)>2]|=(128|63&s)>2]|=(240|s>>18)>2]|=(128|s>>12&63)>2]|=(128|s>>6&63)>2]|=(128|63&s)=64?( this .start=i-64, this .hash(), this .hashed=!0): this .start=i} return this .bytes>4294967295&&( this .hBytes+= this .bytes/4294967296>2]|=EXTRA[3&r],r>=56&&( this .hashed|| this .hash(),t[0]=t[16],t[16]=t[1]=t[2]=t[3]=t[4]=t[5]=t[6]=t[7]=t[8]=t[9]=t[10]=t[11]=t[12]=t[13]=t[14]=t[15]=0),t[14]= this .bytes>29, this .hash()}},Md5.prototype.hash= function (){ var t,r,e,s,i,o,h= this .blocks; this .first?(t=h[0]-680876937,t=(t>>25)-271733879>>20)+t>>15)+s>>10)+e>>25)+r>>20)+t>>15)+s>>10)+e>>25)+r>>20)+t>>15)+s>>10)+e>>25)+r>>20)+t>>15)+s>>10)+e>>25)+r>>20)+t>>15)+s>>10)+e>>27)+r>>23)+t>>18)+s>>12)+e>>27)+r>>23)+t>>18)+s>>12)+e>>27)+r>>23)+t>>18)+s>>12)+e>>27)+r>>23)+t>>18)+s>>12)+e>>28)+r>>21)+t>>16)+s>>9)+e>>28)+r>>21)+t>>16)+s>>9)+e>>28)+r>>21)+t>>16)+s>>9)+e>>28)+r>>21)+t>>16)+s>>9)+e>>26)+r>>22)+t>>17)+s>>11)+e>>26)+r>>22)+t>>17)+s>>11)+e>>26)+r>>22)+t>>17)+s>>11)+e>>26)+r>>22)+t>>17)+s>>11)+e>4&15]+HEX_CHARS[15&t]+HEX_CHARS[t>>12&15]+HEX_CHARS[t>>8&15]+HEX_CHARS[t>>20&15]+HEX_CHARS[t>>16&15]+HEX_CHARS[t>>28&15]+HEX_CHARS[t>>24&15]+HEX_CHARS[r>>4&15]+HEX_CHARS[15&r]+HEX_CHARS[r>>12&15]+HEX_CHARS[r>>8&15]+HEX_CHARS[r>>20&15]+HEX_CHARS[r>>16&15]+HEX_CHARS[r>>28&15]+HEX_CHARS[r>>24&15]+HEX_CHARS[e>>4&15]+HEX_CHARS[15&e]+HEX_CHARS[e>>12&15]+HEX_CHARS[e>>8&15]+HEX_CHARS[e>>20&15]+HEX_CHARS[e>>16&15]+HEX_CHARS[e>>28&15]+HEX_CHARS[e>>24&15]+HEX_CHARS[s>>4&15]+HEX_CHARS[15&s]+HEX_CHARS[s>>12&15]+HEX_CHARS[s>>8&15]+HEX_CHARS[s>>20&15]+HEX_CHARS[s>>16&15]+HEX_CHARS[s>>28&15]+HEX_CHARS[s>>24&15]},Md5.prototype.toString=Md5.prototype.hex,Md5.prototype.digest= function (){ this .finalize(); var t= this .h0,r= this .h1,e= this .h2,s= this .h3; return [255&t,t>>8&255,t>>16&255,t>>24&255,255&r,r>>8&255,r>>16&255,r>>24&255,255&e,e>>8&255,e>>16&255,e>>24&255,255&s,s>>8&255,s>>16&255,s>>24&255]},Md5.prototype.array=Md5.prototype.digest,Md5.prototype.arrayBuffer= function (){ this .finalize(); var t= new ArrayBuffer(16),r= new Uint32Array(t); return r[0]= this .h0,r[1]= this .h1,r[2]= this .h2,r[3]= this .h3,t},Md5.prototype.buffer=Md5.prototype.arrayBuffer,Md5.prototype.base64= function (){ for ( var t,r,e,s= "" ,i= this .array(),o=0;15>o;)t=i[o++],r=i[o++],e=i[o++],s+=BASE64_ENCODE_CHAR[t>>>2]+BASE64_ENCODE_CHAR[63&(t>>4)]+BASE64_ENCODE_CHAR[63&(r>>6)]+BASE64_ENCODE_CHAR[63&e]; return t=i[o],s+=BASE64_ENCODE_CHAR[t>>>2]+BASE64_ENCODE_CHAR[t |
- 登录页面。
- 其他样式及其他js文件不再给,大家只需要关注手机号和密码输入框,还有提交按钮就行,主要在于实现登录。
- 解释一下下面代码主要含义:登录按钮绑定login函数,在该函数中调用了dologin函数,dologin中获取输入框输入密码,然后取出盐值的其中4个字符串然后混合拼接到密码中,然后使用MD5加密。最后使用ajax,将加密后的密码封装到参数中。这样传输过程就不再是明文形式。而ajax是服务器端进行路径控制层拦截处理
login.html:
1 | < title >登录</ title > |
用户登录
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();
}
});
}
服务器端(后端)
引入依赖:
1 | commons-codeccommons-codecorg.apache.commonscommons-lang33.12.0 |
- 封装MD5加密解密工具类
- 解释一下如下代码:首先加密函数是随机生成一个16位盐值,然后与传过来的密码拼接,拼接后进行MD5加密,这个盐值,我们必须要保存的,要不然我们自己也无法解密,所以再将盐值按照自定义的方法插入加密后的密文中。然后在解密时,根据这个方法取出盐值,进行解密操作。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | public class MD5Util { public static String md5(String str){ return DigestUtils.md5Hex(str); } /** * 加密 * 生成盐和加盐后的MD5码,并将盐混入到MD5码中,对MD5密码进行加强 **/ public static String generateSaltPassword(String password) { Random random = new Random(); /** * 生成一个16位的随机数,也就是盐 **/ StringBuilder stringBuilder = new StringBuilder( 16 ); stringBuilder.append(random.nextInt( 99999999 )).append(random.nextInt( 99999999 )); int len = stringBuilder.length(); if (len |
这里我准备了个测试:传过来个前端加密的密码,进行加密,再进行解密验证。
1 2 3 4 5 6 | public static void main(String[] args) { String p1 = generateSaltPassword( "d3b1294a61a07da9b49b6e22b2cbd7f9" ); System.out.println(p1); System.out.println(verifySaltPassword( "d3b1294a61a07da9b49b6e22b2cbd7f9" ,p1)); } |
可以看到,解密加密成功。
登录逻辑处理,注重看登录逻辑,一些工具类不再列出。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | public RespBean doLogin(LoginVo loginVo, HttpServletRequest request, HttpServletResponse response) { String mobile = loginVo.getMobile(); String password = loginVo.getPassword(); // if (StringUtils.isEmpty(mobile) || StringUtils.isEmpty(password)) { // return RespBean.error(RespBeanEnum.LOGIN_ERROR); // } // if (!ValidatorUtil.isMobile(mobile)){ // return RespBean.error(RespBeanEnum.MOBILE_ERROR); // } //根据手机号获取用户 User user = userMapper.selectById(mobile); if ( null == user) { throw new GlobalException(RespBeanEnum.LOGIN_ERROR); } // 获取数据库用户密码 String saltPassword = user.getPassword(); //判断密码是否正确 if (!MD5Util.verifySaltPassword(password, saltPassword)){ throw new GlobalException(RespBeanEnum.LOGIN_ERROR); } //生成cookie String ticket = UUIDUtil.uuid(); redisTemplate.opsForValue().set( "user:" + ticket,user); // request.getSession().setAttribute(ticket,user); CookieUtil.setCookie(request,response, "userTicker" ,ticket); return RespBean.success(); } |
补充
- 随机盐的生成有很多方法,比如生成UUID截取等。
- 如果想更加复杂可以将盐中的字符串按照一定自定义方法插入到传来的密码中,然后再MD5加密,最后再将盐值插入其中。
总结
到此这篇关于SpringBoot+随机盐值+双重MD5实现加密登录的文章就介绍到这了,更多相关SpringBoot 加密登录内容请搜索IT俱乐部以前的文章或继续浏览下面的相关文章希望大家以后多多支持IT俱乐部!