前言

这是初学者的一个必经的坑和坏习惯:数据库连接信息用户名密码在配置文件中是以明文显示

造成这样的原因很简单:熟悉技术需要快速了解核心内容,先学会使用,再学会安全。

看完本章内容,你将会从玩具密码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>

核心步骤

springboot mybatis 数据库敏感字段加密 mybatis 密码加密_安全


加密选择:非对称加密

加密选择只是作者自己的一种加密方式,非对称加密是指会生成两个不同的秘钥:公钥私钥,如果一个用于加密,另外一个就只能用于解密。

其他加密方式还有:对称加密摘要加密等,加密手段。

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));

该程序运行的结果如下图:

springboot mybatis 数据库敏感字段加密 mybatis 密码加密_非对称加密_02

记住!
运行后,我们需要

  • 保存私钥(因为公钥用来加密了)
  • 保存密文(将密文替换明文)
    之后程序拿到密文,拿到私钥,就可以通过私钥要密文解密

为什么可以做到安全呢?

首先用户无法光从密文推理得到明文,其次,秘钥可以通过文件流读取的方式获得,不直接写在程序中。当然服务器安全又是另外的内容了

编写解密的工具类

springboot mybatis 数据库敏感字段加密 mybatis 密码加密_java_03


代码
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;
    }
}

核心解释:

  1. FileInputStream使用文件流的方式读取私钥
  2. RSA rsa = new RSA(key, null)构建一个RSA类对象,只传入私钥(用私钥解密)
  3. rsa.decrypt(decode, KeyType.PrivateKey)使用decrypt方法解密
  4. 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;
}