前言
这是初学者的一个必经的坑和坏习惯:数据库连接信息用户名和密码在配置文件中是以明文显示
造成这样的原因很简单:熟悉技术需要快速了解核心内容,先学会使用,再学会安全。
看完本章内容,你将会从玩具密码root:
jdbc.password=root
进阶成真正的密码!
jdbc.password=KgIKU8yJozGvKRvx0AClN2XJqjd3DrlrcVYTyy0PpFEEymrxGhrln2I6NMO8HjbGezHuTCC/0yDTYNpCENh8q3G60db4RhEGxmLzqMZfBrvTfg/FSWW92+HWX/16fQYjoisjBZn8rnwPrClSc8YX7L2joN+P18odA5ozEMixfVE=
工具HuTool
一个很全的Java工具类库,提高工作效率!
方法需要使用加密算法,而HuTool刚好有加密的API,拿来用即可!
🎈中文文档:入门和安装 (hutool.cn)
使用Maven安装
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.7.22</version>
</dependency>
核心步骤
加密选择:非对称加密
加密选择只是作者自己的一种加密方式,非对称加密是指会生成两个不同的秘钥:公钥和私钥,如果一个用于加密,另外一个就只能用于解密。
其他加密方式还有:对称加密,摘要加密等,加密手段。
TIPS:加密相关的内容并非本文重点。
将明文加密成密文
我们的密码,在初始阶段都是自己设定的,例如root
,当然程序在最终读取密码并进行配置的时候也是读取root
为什么需要密文?
作者遇到过这个情况:我公开的代码中,包含了数据库的用户名和密码,都是以明文暴露的,或者其他类似的隐私数据,直接以明文暴露出来。导致数据库被他人连接,测试。
当密码root
通过加密变成了KgIKU8yJozGvKRvx0AClN2XJqj...
,而其他用户又没有解密的秘钥,就无法通过用户名,密码的方式连接你的数据库了,安全!
代码
// 使用非对称加密:RSA,会自动帮我们生成公钥和私钥
RSA rsa = new RSA();
// 获取私钥【保存】
String privateKeyBase64 = rsa.getPrivateKeyBase64();
System.out.println("私钥:" + privateKeyBase64);
// 获取公钥
String publicKeyBase64 = rsa.getPublicKeyBase64();
System.out.println("公钥:" + publicKeyBase64);
// 使用公钥将密码root加密,变成密文
byte[] encrypt = rsa.encrypt(StrUtil.bytes("root"), KeyType.PublicKey);
// 将密文用Base64编码,防止出现乱码,这个输出的密文,【我们保留使用】
String encode = Base64.encode(encrypt);
System.out.println("使用公钥加密后:" + StrUtil.str(encode,CharsetUtil.CHARSET_UTF_8));
// 将Base64编码后的密文,通过Base64反编译回去
byte[] decode = Base64.decode(encode);
// 使用私钥进行解密
byte[] decrypt = rsa.decrypt(decode, KeyType.PrivateKey);
// 将解密的内容,使用字符串显示出来
System.out.println("使用私钥解密后:" + StrUtil.str(decrypt,CharsetUtil.CHARSET_UTF_8));
该程序运行的结果如下图:
记住!
运行后,我们需要
- 保存私钥(因为公钥用来加密了)
- 保存密文(将密文替换明文)
之后程序拿到密文,拿到私钥,就可以通过私钥要密文解密
为什么可以做到安全呢?
首先用户无法光从密文推理得到明文,其次,秘钥可以通过文件流读取的方式获得,不直接写在程序中。当然服务器安全又是另外的内容了
编写解密的工具类
代码
public class Decrypt {
public static String decrypt(String encode) throws IOException {
String decode = null;
// 获取传入的密文
decode = encode;
// 读取秘钥文件
FileInputStream inputStream = new FileInputStream("src/main/resources/privateKey");
BufferedInputStream bufferedInputStream = new BufferedInputStream(inputStream);
byte[] bytes = new byte[1024];
int len = 0;
String key = null;
// 得到秘钥文件
while ((len = bufferedInputStream.read(bytes))!=-1){
key = new String(bytes, 0, len);
}
// 通过RSA解密,只需需要传入秘钥即可
RSA rsa = new RSA(key, null);
// 将密文使用Base64编码反编译
byte[] decryptCode = Base64.decode(decode);
// 解密
byte[] decrypt = rsa.decrypt(decode, KeyType.PrivateKey);
// 将解密的内容,使用字符串显示出来
String res = StrUtil.str(decrypt, CharsetUtil.CHARSET_UTF_8);
bufferedInputStream.close();
inputStream.close();
// 返回明文
return res;
}
}
核心解释:
-
FileInputStream
使用文件流的方式读取私钥 -
RSA rsa = new RSA(key, null)
构建一个RSA类对象,只传入私钥(用私钥解密) -
rsa.decrypt(decode, KeyType.PrivateKey)
使用decrypt方法解密 -
StrUtil.str(decrypt, CharsetUtil.CHARSET_UTF_8)
将解密的内容转成String字符串并返回
在SSM框架中使用
使用了密文替换明文
jdbc.password=KgIKU8yJozGvKRvx0AClN2XJqjd3DrlrcVYTyy0PpFEEymrxGhrln2I6NMO8HjbGezHuTCC/0yDTYNpCENh8q3G60db4RhEGxmLzqMZfBrvTfg/FSWW92+HWX/16fQYjoisjBZn8rnwPrClSc8YX7L2joN+P18odA5ozEMixfVE=
构造Druid数据池时,解密后加载
@Bean
public DataSource dataSource(){
DruidDataSource dataSource = new DruidDataSource();
dataSource.setDriverClassName(driverClassName);
dataSource.setUrl(url);
dataSource.setUsername(userName);
dataSource.setPassword(getDecryptPassword(password));
dataSource.setInitialSize(initialSize);
return dataSource;
}
private String getDecryptPassword(String password){
try {
// 这里使用了我们的解密方式
return Decrypt.decrypt(password);
} catch (IOException e) {
e.printStackTrace();
}
return null;
}