已注销用户的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头像
最近浏览
我是helloworld  LV23 2023年5月17日
ls2008  LV15 2022年9月28日
best2018  LV46 2022年6月30日
zhangbendong  LV11 2022年5月30日
tiantian459  LV11 2021年8月31日
ChenXingyu  LV13 2021年7月16日
阳春夜雪  LV4 2021年3月8日
sweetbox  LV10 2021年2月15日
hujiansir  LV8 2020年12月29日
wpp1998  LV2 2020年11月25日
顶部 客服 微信二维码 底部
>扫描二维码关注最代码为好友扫描二维码关注最代码为好友