这篇文章也是在做项目的时候使用到的内容,来做成一篇博客
(一)加密是什么?
我们在https中也说到了加密,因为https就是http加密后的产物,当时又说到了运营商劫持,然后引出加密,然后加密的方法---对称加密,又因为要传输密钥,引入非对称加密(公钥私钥),但是又可能会有中间人攻击(中间人伪造成服务器)又引入了证书一系列的东西
上述是基于http加密去讲解的,那这里我们再来基于密码加密讲解一下
在mysql数据库中,我们就需要对密码,手机号这些敏感的信息进行加密,因为如果使用明文存储时,当用户侵入数据库,就可以直接全部获取到,我们为了避免这种情况就需要对存入的隐私数据进行加密
密码算法分类
那密码算法主要分为三类:对称密码算法,非对称密码算法,摘要算法
对称密码算法:就是加密密钥和解密密钥一样的密码算法,常见的对称密码算法:RC4,RC5,AES,DES等
非对称密码算法:就是加密密钥和解密密钥不一样的密码算法,分为公钥和私钥,该算法就是把公钥放出去,你们通过公钥加密然后把信息传给我,我再通过密钥解密,常见的非对称密码算法:RSA,DSA等
摘要算法:就是把任意长度的输入消息数据转化为固定长度的输出数据的算法,这个算法是不可逆的,也就是不可解密,所以这个算法一般只用来判断是否相等,我们如果使用这算法存数据,我们自己在数据库中就也不知道真实的值(所以可以用来在数据库中存密码,即使被黑了数据库,他也不知道密码),常见的摘要算法:MD5,SHA,CRC等
加密思路
这里我们使用md5算法进行加密
但是还有一个问题就是,虽然md5加密后的密文无法解密,但是相同的密码,生成的MD5数据是一样的,然后有一些用户的密码是很短的,这就会导致他们的密码会重复,又因为很短导致黑客可以相对简单的破解,所以我们要考虑对用户输入的密码进行一层包装,然后再进行加密
那我们加一层包装要怎么加?像上篇博客中JWT令牌那样,生成一个随机字符串后,把他变为静态的常量,放到密码前包装一下进行加密?虽然这样是可以加强用户密码的复杂度,大大加强了黑客的破解难度,但是还是存在一个问题就是破解了一个用户的信息,就会更容易破解其他用户的信息。
解决方案:
我们这里采用的是密码拼接“随机”字符串来进行加密,这个随机字符我们称之为‘盐’,这样黑客拿到了这个加密串就不是很好破解了
上述图还少了最后一步,加密算法后我们要保存我们的盐值方便我们下一次解密,所以我们服务器存储的是盐值+密文
解密流程:
因为md5不可逆,所以只能我们是通过判断盐值+用户输入的密码进行加密算法后得到的数据,是否与密文相同
(二)写加密/解密工具类
1.对密码进行加密
public static String encrypt(String password){String salt = UUID.randomUUID().toString() //生成随机盐值.replace("-", ""); //将生成字符串中间的-替换为“”String finalPassword =DigestUtils.md5DigestAsHex((salt + password).getBytes());return salt+finalPassword; //返回盐值+(盐值和密码生成的字符串)}
这里的uuid生成了随机盐值 是这样的,当我们把-去掉,固定的长度就是32位了,正好与我们MD5,算法生成的随机字符串长度相同
然后我们通过MD5算法把盐值+密码生成了一个密文,并返回盐值+密文
2.对密码进行解密
我们上面说生成的盐值是32位,md5生成的密文也是32位的,那返回的盐值+密文就是64位,如果不是那就是有错误了
然后我们拿到前32的盐值,并且跟我们传入的密码进行加密然后拼接盐值,判断和数据库中存入的密码是否相等,如果相等的话,就说明密码是正确的
public static Boolean verify(String password,String finalPassowrd){if(!StringUtils.hasLength(password)||!StringUtils.hasLength(finalPassowrd)){return false;}if(finalPassowrd.length()!=64)return false;String salt=finalPassowrd.substring(0,32);String thisPassword = DigestUtils.md5DigestAsHex((salt + password).getBytes());return (salt+thisPassword).equals(finalPassowrd);}