最代码伊成的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 配置文件即可。

 

最后说两句

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

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

     


打赏

已有1人打赏

最代码官方的gravatar头像

分享到:

最近浏览
落叶似从容59分钟前
最代码贡献等级说明
wy93166537410小时前
最代码贡献等级说明
dorlayu昨天
最代码贡献等级说明
暂无贡献等级
ndybaidu1月21日
暂无贡献等级
源滚滚1月21日
最代码贡献等级说明
hewhlster1月21日
暂无贡献等级
shijiangchen1月19日
暂无贡献等级
aa6678991月19日
最代码贡献等级说明
hackxhao1月19日
最代码贡献等级说明
10769164051月19日
最代码贡献等级说明
annie791月19日
暂无贡献等级
smfx13141月19日
最代码贡献等级说明
暂无贡献等级
mjzxcyypp1月19日
最代码贡献等级说明
法Q蛮LWBB1月18日
暂无贡献等级
攻克己身1月18日
最代码贡献等级说明
顶部客服微信二维码底部
>扫描二维码关注最代码为好友扫描二维码关注最代码为好友