在前面的章节中,我们实现了简易的注册和登录功能,其它功能,像验证码、限流等或许你稍微研究一下都能找到解决方案,但有一个点你可能不那么容易搞定,那就是注册登录密码如何安全传输?
可以肯定的是:密码一定需要加密,一定不能明文传输!但加密算法怎么选?密码又要怎么安全保存?
如果你没有一些项目经验,估计有点棘手,最后还会感觉不那么保险。难者不会, 会者不难!
基于此,我今天决定写一篇简短的博文,手把手教你在SpringBoot实战项目中解决密码传输和密码保存的问题!
最后看完本文,你也看看你们的项目是否安全传输密码,是否安全保存密码!
之前讲过的注册和登录接口相关博文:
登录接口:3-3. SpringBoot项目集成【用户身份认证】实战 【全流程篇】基于JWT+双重检查的登录+登出+拦截器
注册接口:5.4 如何在Mybatis中使用insert标签并返回主键id?-- 教你通用做法
回顾一下之前写的流程:
注册:前端:
MD5(password),后端:
存储前端传入的密码!
登录:前端:
MD5(password),后端:
将前端传入的密码,与数据库保存的密码做匹配,匹配成功则登录成功!
看出来问题了吗?
如果是内部系统,部署在内网上,大家都是合法调用,那应该会相安无事!
但如果是部署到互联网上,那随时可能面临恶意调用的风险,后端既没有对密码的有校性做校验,也没有对密码的合法性做验证,说白了,后端连密码的控制权都没有,黑客传个明文的123456你一点办法都没有!
认识到问题就等于解决了一半!
第一,注册时的用户填密码绝对不可以直接明文传输到后端,前端必须加密,还必须支持让后端可以解密,为什么?
因为后端需要对密码有效性做校验,例如:8-16位字符,必须包含大、小写字母及数字
!
第二,后端对有效性校验通过以后,必须保存不能解密的密码,为什么?
如果数据库保存的密码能解密,那么开发人员,DBA,黑客等,是不是都有可能看到用户的密码?所以,安全的做法是:数据库保存的是不可解密的密码!
针对第一点,我们可以采用AES或RSA等加密算法,相对安全又可以解密,我推荐是RSA,因为它是非对称加密!非对称的意思是指加密和解密的秘钥是不同的!
值得说的是,RSA加密算法的安全性是较高的,非常值得信赖,位数越长越安全,没有秘钥破解非常困难!
另外,由于AES是对称加密,前端加密与后端解密的秘钥相同,所以这个秘钥保存在哪里也是一个问题,如何防止秘钥泄露也是一个问题!因为拿到这唯一的秘钥就可以解密了!!!而使用RSA加密算法,即使公钥泄露也不怕,因为RSA是非对称加密,公钥加密,私钥才可以解密!而私钥保存在后端呢,所以安全性更高一些!
针对第二点,保存不可解密的密码,众所周知的是MD5,只要你的密码不是太简单,就很难被暴力破解!妥妥滴~
我这里使用hutool提供的现成的加密方法,先在父项目pom.xml dependencyManagement.dependencies
引入依赖:
<dependency>
<groupId>cn.hutoolgroupId>
<artifactId>hutool-allartifactId>
<version>5.8.12version>
dependency>
再到common子项目引入依赖(不需要版本号)
初始化RSA类
RSA rsa = new RSA();
生成公钥、私钥
String privateKeyBase64 = rsa.getPrivateKeyBase64();
String publicKeyBase64 = rsa.getPublicKeyBase64();
使用公钥加密
:
String encryptStr = rsa.encryptBase64("123456", KeyType.PublicKey);
使用私钥解密
:
String decryptStr = rsa.decryptStr(encryptStr, KeyType.PrivateKey);
String md5Str = MD5.create().digestHex("123456")
完整的测试代码:
public static void main(String[] args) {
// 初始化RSA类
RSA rsa = new RSA();
// 生成公钥、私钥
String privateKeyBase64 = rsa.getPrivateKeyBase64();
String publicKeyBase64 = rsa.getPublicKeyBase64();
// 公钥加密
String encryptStr = rsa.encryptBase64("123456", KeyType.PublicKey);
System.out.println(encryptStr);
// 私钥解密
String decryptStr = rsa.decryptStr(encryptStr, KeyType.PrivateKey);
System.out.println(decryptStr);
// md5加密
String md5Str = MD5.create().digestHex(decryptStr);
System.out.println(md5Str);
}
3-2. SpringBoot项目集成【用户身份认证】实战 【实战核心篇】基于JWT生成和校验Token
在【3-2】的第五段 已经定义了配置类,也在配置文件定义了rsa的公钥私钥配置
,同理把RSA也注入进来!
在AuthServiceImpl
中注入`RSA:
此时,我们接收到的是使用RSA公钥
加密的密码,所以:
RSA私钥
解密,再做密码有效性校验,最后MD5加密保存到数据库RSA私钥
解密,再MD5加密,与数据库保存的密码对比基于此,我封装了这个方法(注释够清晰了吧?):
/**
* 将用RSA公钥加密的密码,转为MD5加密的密码
*
**/
private String rsaToMd5(String password, boolean isRegister) {
// 1. RSA私钥解密
String decryptPwd;
try {
decryptPwd = rsa.decryptStr(password, KeyType.PrivateKey);
} catch (Exception e) {
log.error("rsaToMd5异常:{}", e.getMessage(), e);
return null;
}
// 2. 如果是注册:密码有效性校验
if (isRegister) {
// 密码有效性校验,例如:8-16位字符,必须包含大、小写字母及数字
// 这里实现你想定义的校验规则
}
// 3. MD5加密
return MD5.create().digestHex(decryptPwd);
}
注册接口:
调rsaToMd5方法
用Assert做个密码校验
登录接口:
和注册类似,调rsaToMd5方法
用Assert做个密码校验
注意这里的Assert方法参数里多了个code,是新增的,这也是自定义Assert的好处,随心所欲:
/**
* obj不能为空,如果是空则抛出异常
**/
public static void notNull(Object obj, String code, String msg) {
if (isNullOrBlank(obj)) {
throw new BizException(code, msg);
}
}
当然了,加密算法你可以任意组合使用,比如:RSA+AES+MD5,或者AES+RSA+MD5等等还可以嵌套调用,无数种方案。。。
看到这,觉得有帮助的,刷波666,感谢大家的支持~
想要看更多实战好文章,还是给大家推荐我的实战专栏–>《基于SpringBoot+SpringCloud+Vue前后端分离项目实战》,由我和 前端狗哥 合力打造的一款专栏,可以让你从0到1快速拥有企业级规范的项目实战经验!
具体的优势、规划、技术选型都可以在《开篇》试读!
订阅专栏后可以添加我的微信,我会为每一位用户进行针对性指导!
另外,别忘了关注我:天罡gg ,怕你找不到我,发布新文不容易错过: https://blog.csdn.net/scm_2008