DruidPasswordCallback配置方法及分析

DruidPasswordCallback使用方法及流程分析

功能介绍:

DruidPasswordCallback是druid提供的用于数据库加密的类,原因:直接将数据库密码写在配置文件中增加了数据库秘密泄露的风险。

配置方式介绍:

  1. 配置连接池
    <!-- 自定义的设置数据库密码类 -->
    <bean id = "dbPasswordCallback" class="cn.andrew.until.DBPasswordCallback"/>

    <!-- mysql数据源配置 -->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
        <!-- 基本属性 url、user、password -->
        <property name="url" value="${mysql.url}" />
        <property name="driverClassName" value="${mysql.driverClassName}"/>
        <property name="username" value="${mysql.username}" />
        <!-- 这一行可以不需要了-->
        <!--<property name="password" value="${mysql.password}" />-->
        <!-- 配置初始化大小、最小、最大 -->
        <property name="initialSize" value="1" />
        <property name="minIdle" value="1" />
        <property name="maxActive" value="20" />

        <!-- 配置获取连接等待超时的时间 -->
        <property name="maxWait" value="60000" />

        <!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->
        <property name="timeBetweenEvictionRunsMillis" value="60000" />

        <!-- 配置一个连接在池中最小生存的时间,单位是毫秒 -->
        <property name="minEvictableIdleTimeMillis" value="300000" />

        <property name="validationQuery" value="SELECT 'x'" />
        <property name="testWhileIdle" value="true" />
        <property name="testOnBorrow" value="false" />
        <property name="testOnReturn" value="false" />

        <!-- 打开PSCache,并且指定每个连接上PSCache的大小 -->
        <property name="poolPreparedStatements" value="true" />
        <property name="maxPoolPreparedStatementPerConnectionSize" value="20" />

        <!-- 配置监控统计拦截的filters -->
        <property name="filters" value="stat" />
        <!-- 配置文件中的password应该是加密后的密码-->
        <property name="connectionProperties" value="password=${mysql.password}"/>
        <property name="passwordCallback" ref="dbPasswordCallback"/>
    </bean>

需要注意的是 dbPasswordCallback bean配置以及DataSource bean的password,connectionProperties,passwordCallback三个属性。
2. 生成密钥对(我这使用RSA算法加密,如果使用其他算法可以跳过这一步):

public class KeyPairGenUtil {
    //指定加密算法为RSA
    private static final String ALGORITHM = "RSA";
    //密钥长度,用来初始化
    private static final int KEYSIZE = 1024;
    //指定公钥存放文件
    private static String PUBLIC_KEY_FILE = "c://test/pub.k";
    //指定私钥存放文件
    private static String PRIVATE_KEY_FILE = "c://test/pri.k";


    public static void main(String[] args) throws Exception {
        generateKeyPair();
        encrypt("root")
    }

    /**
     * 生成密钥对,druid中的ConfigTools类中也有生成方法可以直接调用
     * @throws Exception
     */
    private static void generateKeyPair() throws Exception {

        // 为RSA算法创建一个KeyPairGenerator对象
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(ALGORITHM);

        keyPairGenerator.initialize(KEYSIZE);

        //生成密匙对
        KeyPair keyPair = keyPairGenerator.generateKeyPair();

        // 得到公钥
        Key publicKey = keyPair.getPublic();

        // 得到私钥
        Key privateKey = keyPair.getPrivate();

        FileOutputStream fos1 = null;
        FileOutputStream fos2 = null;
        try {
            //用文件流将生成的密钥写入文件
            fos1 = new FileOutputStream(PUBLIC_KEY_FILE);
            fos2 = new FileOutputStream(PRIVATE_KEY_FILE);
            fos1.write(Base64.byteArrayToBase64(publicKey.getEncoded()).getBytes());
            fos2.write(Base64.byteArrayToBase64(privateKey.getEncoded()).getBytes());
        } catch (Exception e) {
            throw e;
        } finally {
            //清空缓存,关闭文件输出流
            fos1.close();
            fos2.close();
        }
    }

 private String encrypt(String text) throws Exception {
         //读取私钥
        FileInputStream in2 = new FileInputStream(new File(PRIVATE_KEY_FILE));
        //如果改变key的长度这里需要适当调整
        byte[] buffer = new byte[2048];

        int count = in2.read(buffer);
        String privateKey = new String(buffer,0,count);

        //加密
        String code = ConfigTools.encrypt(privateKey,text);
        System.out.println(code);
        return code;
    }
}
  1. DBPasswordCallback类具体实现:
/**
*需要继承DruidPasswordCallback  并重写setProperties 方法
**/
public class DBPasswordCallback extends DruidPasswordCallback {

    //我使用的是RSA加密算法,所以讲公钥放在这的
    private final String publicKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCsolr7//J3YqPkYGvkzL/+zoBbIDbKvYd1zm/mgHHKCHdP1oKA9L9BzfoRW6u3bf7lmUidS43eO0n7x34ThGY22NT5orwkFQ4+MuuSZve5XH+OxXVnLdWYw7WVqI0mCS52cNfQWBWjUNYAPGA+Zy0V8FHhlutBzNrb/b0YE2fCsQIDAQAB";
    @Override
    public void setProperties(Properties properties) {
        super.setProperties(properties);
        //获取配置文件中加密后的密码,和xml中的connectionProperties属性配置相关
        String password = (String) properties.get("password");
        try {
            //解密过程,ConfigTools为druid自带,提供一些好用的函数
            String dbpassword= ConfigTools.decrypt(publicKey,password);
            //设置密码
            setPassword(dbpassword.toCharArray());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

到这里就算配置成功了。如果你想知道为什么这么配置下面有一个简单的流程分析。

核心流程分析

主要代码在 DruidDataSource类的父类DruidAbstractDataSource类的createPhysicalConnection函数中核心代码:

        //获取密码,按照上面的配置这里的返回值是null
        String password = getPassword();
        //返回我们知己定义的PasswordCallback 即DBPasswordCallback
        PasswordCallback passwordCallback = getPasswordCallback();

        if (passwordCallback != null) {
            //这里就是DBPasswordCallback继承自DruidPasswordCallback的原因
            //结合这些代码代码你可以想到去继承DruidPasswordCallback的父类PasswordCallback 但这样需要去重定义他的构造函数
            if (passwordCallback instanceof DruidPasswordCallback) {
                DruidPasswordCallback druidPasswordCallback = (DruidPasswordCallback) passwordCallback;

                druidPasswordCallback.setUrl(url);
                //数据源中的connectionProperties,选择重写DruidPasswordCallback setProperties方法的原因
                druidPasswordCallback.setProperties(connectProperties);
            }

            //获得在DBPasswordCallback中调用set方法设置的密码
            char[] chars = passwordCallback.getPassword();
            if (chars != null) {
                password = new String(chars);
            }
        }
  • 2
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值