springboot的配置文件加解密-安全性问题

1,555 阅读2分钟

篇序:springboot开发过程中,需要配置很多信息在配置文件里,如果数据不加密,传明文,如数据库什么的就直接暴露了,存在安全隐患,因此需要进行加密处理才行。配置文件是密文,在代码实际使用过程中解密信息成明文,供程序使用。 这里有一个别人已经封装完善的jar包工具, jaypt大家有兴趣的话可以自行了解,因为他封装的代码太多,我这边只记录我自己的理解使用。 没有那么通用性,扩展的话,可以向其学习,多了解下源码,我这的可以直接搬砖。 注意:有很多什么如对称加密算法,非对称加密算法,什么加盐salt之类的,就自行了解了,推荐几种:AES、RSA、ECC、SHA。 安全性更高之类的,增加伪随机数,想学习的可以研究下。 前提要有自己的加解密工具类,我用的AES。

配置示例

demo:
  password: ENC(nGkRyFhFQoicg4xtPtF5ag==)

开始

关键类BeanFactoryPostProcessor

/**
 * 定义一个属性注入替换
 */
public class EnableEncryptablePropertiesBeanFactoryPostProcessor implements BeanPostProcessor, Ordered {

    private final ConfigurableEnvironment environment;
    private final EncryptablePropertyResolver encryptablePropertyResolver;

    public EnableEncryptablePropertiesBeanFactoryPostProcessor(ConfigurableEnvironment environment, EncryptablePropertyResolver encryptablePropertyResolver) {
        this.environment = environment;
        this.encryptablePropertyResolver = encryptablePropertyResolver;
    }

    @Override
    public Object  postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        MutablePropertySources propertySources = environment.getPropertySources();
        propertySources.forEach(pv -> {
            PropertySource propertySource = propertySources.get(pv.getName());
            if(propertySource instanceof MapPropertySource){
                Map<String, Object> convertPropertySource = new HashMap<String, Object>();
                MapPropertySource newMapPropertySource = new MapPropertySource(pv.getName(), convertPropertySource);
                String[] propertyNames = ((MapPropertySource) propertySource).getPropertyNames();
                for (String propertyName : propertyNames) {
                    Object value = propertySource.getProperty(propertyName);
                    if(value instanceof  String){
                        convertPropertySource.put(propertyName, encryptablePropertyResolver.resolvePropertyValue((String)value));
                    }else{
                        convertPropertySource.put(propertyName, value);
                    }
                }
                propertySources.replace(pv.getName(), newMapPropertySource);
            }
        });
    }

    @Override
    public int getOrder() {
        return Ordered.LOWEST_PRECEDENCE - 100;
    }
}

将其类和加密工具方法注入spring的容器管理

@Configuration
public class LocalPropertySourcesPlaceholderConfigurer   {

    @Bean
    public EncryptablePropertyResolver encryptablePropertyResolver(){
        return new DefalutPropertyResolver(new DefalutEncryptor());
    }

    @Bean
    public EnableEncryptablePropertiesBeanFactoryPostProcessor enableEncryptablePropertiesBeanFactoryPostProcessor(
            final ConfigurableEnvironment environment, EncryptablePropertyResolver encryptablePropertyResolver){
        return new EnableEncryptablePropertiesBeanFactoryPostProcessor(environment, encryptablePropertyResolver);
    }

}

解密过程

public class DefalutPropertyResolver implements EncryptablePropertyResolver {

    /**
     * 扩展性:
     * 像这些可以单独弄在配置文件里,便于扩展,也可以配置属于自己的加接密类,进行解密。
     * 封装成自己的jar工具包,增强提供通用性。
     */
    private StringEncryptor encryptor;
    private String prefix = "ENC(";
    private String suffix = ")";

    public DefalutPropertyResolver(StringEncryptor encryptor) {
        this.encryptor = encryptor;
    }

    @Override
    public String resolvePropertyValue(String value) {
        return Optional.ofNullable(value)
                .filter(this::isEncrypted)
                .map(resolvedValue -> {
                    String unwrappedProperty = resolvedValue.trim().substring(prefix.length(), resolvedValue.length() - suffix.length());
                    return encryptor.decrypt(unwrappedProperty);

                })
                .orElse(value);
    }

    private boolean isEncrypted(String property){
        if(property == null){
            return false;
        }
        final String trimValue= property.trim();
        return (trimValue.startsWith(prefix) &&
                trimValue.endsWith(suffix));
    }
}

结语

像这种加密类的,可以自己封装成通用的jar工具通用项目,不用每次新项目都要重新敲一遍,因为自己懒。 像jasypt虽然封装的很好,但是源码太多太乱了,还是自己封装一遍会好一点,根据自己的需要来取用。