最代码伊成的gravatar头像
最代码伊成2018-01-12 11:00:19
如何对spring中数据库properties配置文件的用户名及密码进行加密解密

背景:在此之前,开发过的项目 用户名和密码都是配置在properties文件中,基本上都是明文的形式。

举个栗子 ↓

db1.jdbc.driver=com.mysql.jdbc.Driver
db1.jdbc.url=jdbc:mysql://127.0.0.1:3306/dev?useUnicode=true&characterEncoding=UTF-8
db1.jdbc.username=root
db1.jdbc.password=root

正是因为 用户名和密码都是明文存储在配置文件中,在公司的安全扫描中,就扫出来了这样的一个漏洞。

 

解决方案:

最简单的一个方法就是把配置文件里面的用户名和密码进行加密,这样这个问题就解决了。思路有了,但是还得要搞清楚一个问题,在配置文件进行加密的密文要解密的,所以这里只能选择对称加密解密的算法。这里我选择的是AES !

 

具体实现:

第一种方法:在spring中找到PropertyResourceConfigurer 类中覆盖 convertProperty 方法即可。

源码如下 ↓

	/**
	 * Convert the given property from the properties source to the value
	 * which should be applied.
	 * <p>The default implementation calls {@link #convertPropertyValue(String)}.
	 * @param propertyName the name of the property that the value is defined for
	 * @param propertyValue the original value from the properties source
	 * @return the converted value, to be used for processing
	 * @see #convertPropertyValue(String)
	 */
	protected String convertProperty(String propertyName, String propertyValue) {
		return convertPropertyValue(propertyValue);
	}

具体实现代码 如下

public class EncryptPropertyPlaceholderConfigurer extends PropertyPlaceholderConfigurer {
   //  第一种方式
 private String[] encryptPropNames = {"jdbc.user", "jdbc.password"};


    @Override
    protected String convertProperty(String propertyName, String propertyValue)
    {
        String key = "AkxfGVoMiKDxUiJM";
        String iv = "1841611841611010";

        //如果在加密属性名单中发现该属性
        if (isEncryptProp(propertyName))
        {
            String decryptValue = AesUtil.decrypt(propertyValue,key,iv);
            System.out.println("--------------------------------"+decryptValue);
            return decryptValue;
        }else {
            return propertyValue;
        }

    }

    private boolean isEncryptProp(String propertyName)
    {
        for (String encryptName : encryptPropNames)
        {
            if (encryptName.equals(propertyName))
            {
                return true;
            }
        }
        return false;
    }

 

第二种方法:同样还在这PropertyResourceConfigurer 类中找到 processProperties 方法

源码如下 ↓

	/**
	 * Visit each bean definition in the given bean factory and attempt to replace ${...} property
	 * placeholders with values from the given properties.
	 */
	@Override
	protected void processProperties(ConfigurableListableBeanFactory beanFactoryToProcess, Properties props)
			throws BeansException {

		StringValueResolver valueResolver = new PlaceholderResolvingStringValueResolver(props);
		doProcessProperties(beanFactoryToProcess, valueResolver);
	}

具体实现代码 如下

public class EncryptPropertyPlaceholderConfigurer extends PropertyPlaceholderConfigurer {

    // 第二种方式
    protected void processProperties(ConfigurableListableBeanFactory beanFactory, Properties props)
            throws BeansException {

        String key = "AkxfGVoMiKDxUiJM";
        String iv = "1841611841611010";

        try {
            String userKey = "jdbc.user";
            String passwordKey = "jdbc.password";

            String user = props.getProperty(userKey);
            String password = props.getProperty(passwordKey);

            if(StringUtil.isNotEmpty(user)){
                props.setProperty(userKey,AesUtil.decrypt(user,key,iv));//解密user
            }

            if(StringUtil.isNotEmpty(password)){
                props.setProperty(passwordKey,AesUtil.decrypt(password,key,iv));
            }

            super.processProperties(beanFactory, props); //

        }catch (Exception e){
            throw new BeanInitializationException(e.getMessage());
        }


    }

 

实现代码 就以上两种方式 需要看的更懂的牛牛们 可以自行阅读PropertyResourceConfigurer  这个类。

这个类路径为 

package org.springframework.beans.factory.config;

 

说完实现代码,在看看 配置文件就变成了如下

jdbc.jdbcUrl=jdbc:mysql://localhost:3306/ssm_crud?characterEncoding=utf8
jdbc.driverClass=com.mysql.jdbc.Driver
jdbc.user=B2BA245DBD8B41BDA85B68BF28679511
jdbc.password=B2BA245DBD8B41BDA85B68BF28679511

对用户名 和 密码 进行了加密,具体加密代码 我贴出来,这里就不多累赘了!

/**
 * AES加密工具类
 */
public class AesUtil {


    /**
     * AES加密
     *
     * @param content
     * @param key
     * @param iv
     * @return
     */
    public static String encrypt(String content, String key, String iv) {
        if (StringUtils.isEmpty(key)) {
            return null;
        }
        if (key.length() != 16) {
            return null;
        }
        String encryptText = "";
        try {
            SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes(), "AES");
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");

            IvParameterSpec ivSpec = new IvParameterSpec(iv.getBytes());
            cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivSpec);
            byte[] encrypted = cipher.doFinal(content.getBytes());
            encryptText = parseByte2HexStr(encrypted);
        } catch (IllegalBlockSizeException e) {
        } catch (BadPaddingException e) {
        } catch (InvalidKeyException e) {
        } catch (InvalidAlgorithmParameterException e) {
        } catch (NoSuchAlgorithmException e) {
        } catch (NoSuchPaddingException e) {
        }
        return encryptText;
    }

    /**
     * AES解密
     *
     * @param content
     * @param key
     * @param iv
     * @return
     */
    public static String decrypt(String content, String key, String iv) {
        if (StringUtils.isEmpty(key)) {
            return "AES加密KEY为空!";
        }
        if (key.length() != 16) {
            return "AES加密KEY长度必须16位!";
        }
        String decryptText = "";
        try {
            SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes(), "AES");
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            IvParameterSpec ivSpec = new IvParameterSpec(iv.getBytes());
            cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivSpec);
            byte[] byteContent = parseHexStr2Byte(content);
            byte[] original = cipher.doFinal(byteContent);
            decryptText = new String(original);
        } catch (NoSuchAlgorithmException e) {
        } catch (NoSuchPaddingException e) {
        } catch (InvalidAlgorithmParameterException e) {
        } catch (InvalidKeyException e) {
        } catch (BadPaddingException e) {
        } catch (IllegalBlockSizeException e) {
        }
        return decryptText;
    }


    /**
     * 将二进制转换成16进制
     *
     * @param buf
     * @return sb
     */
    public static String parseByte2HexStr(byte buf[]) {
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < buf.length; i++) {
            String hex = Integer.toHexString(buf[i] & 0xFF);
            if (hex.length() == 1) {
                hex = '0' + hex;
            }
            sb.append(hex.toUpperCase());
        }
        return sb.toString();
    }

    /**
     * 将16进制转换为二进制
     *
     * @param hexStr
     * @return result
     */
    public static byte[] parseHexStr2Byte(String hexStr) {
        if (hexStr.length() < 1) {
            return null;
        }
        byte[] result = new byte[hexStr.length() / 2];
        for (int i = 0; i < hexStr.length() / 2; i++) {
            int high = Integer.parseInt(hexStr.substring(i * 2, i * 2 + 1), 16);
            int low = Integer.parseInt(hexStr.substring(i * 2 + 1, i * 2 + 2), 16);
            result[i] = (byte) (high * 16 + low);
        }
        return result;
    }

    public static void main(String[] args) {
        String key = "AkxfGVoMiKDxUiJM";
        String iv = "1841611841611010";
        String str = encrypt("root", key, iv);
        System.out.println(str);


        System.out.println(decrypt(str, key, iv));
    }

}

tips: 具体实现代码中的 key 是加密的key ; iv 为偏移量

 

最后一步

<bean id="propertyConfigurer" class="com.atguigu.crud.common.EncryptPropertyPlaceholderConfigurer">
		<property name="locations">
			<list>
				<value>classpath:dbconfig.properties</value>
			</list>
		</property>
	</bean>

配置到spring 配置文件即可。

 

最后说两句

这篇博客仅仅作为工作纪录,如有写的不妥之处,望指出。

文明评论,共建文明网络环境!!!

     


打赏

已有2人打赏

LHJ123的gravatar头像最代码官方的gravatar头像

分享到:

最近浏览
暂无贡献等级
暂无贡献等级
暂无贡献等级
warm5184月21日
最代码贡献等级说明
nj1tony4月20日
暂无贡献等级
diy88996464月20日
暂无贡献等级
智慧树qwwe4月19日
最代码贡献等级说明
sevenxiyi4月17日
最代码贡献等级说明
huaua76764月17日
暂无贡献等级
顶部客服微信二维码底部
>扫描二维码关注最代码为好友扫描二维码关注最代码为好友