写作目的
数据安全这块还是挺严重的,尤其是自己专注于业务开发,不能总停留在一个地方,还要关注其他的一些问题,比如数据安全。
配置脱敏
实现配置的脱敏我使用了Java的一个加解密工具Jasypt。该工具支持对称加密和非对称加密。
 首先通过简单的demo配置进行配置和测试。
1、首先引入jasypt-spring-boot-starter
<!--配置文件加密-->
 <dependency>
     <groupId>com.github.ulisesbocchio</groupId>
     <artifactId>jasypt-spring-boot-starter</artifactId>
     <version>2.1.0</version>
 </dependency>
2、接下来就是application的配置,如下面代码所示。其中password是密钥,即类似于MD5盐值加密的盐;prefix和suffix*为判断要解密的规则条件或正则表达式
jasypt:
  encryptor:
    password: demo   # 秘钥
    property:
      prefix: "abc["  #前缀
      suffix: "]"     #后缀
3、配置好后我们就需要在配置文件中配置加密后的数据了,如下面的数据库密码
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/demo?characterEncoding=utf8&useUnicode=true&useSSL=false&serverTimezone=GMT%2B8
    username: root
    password: abc[B6jeXwl1AotiulW1vfsKmQ==]   ### 前缀和后缀包围着密文
    driver-class-name: com.mysql.cj.jdbc.Driver
    type: com.alibaba.druid.pool.DruidDataSource
那么这串密文是怎么来的呢?
    System.out.println("--------------加密----------------");
    StandardPBEStringEncryptor standardPBEStringEncryptor = new StandardPBEStringEncryptor();
    // 秘钥
    standardPBEStringEncryptor.setPassword("demo");
    // 明文进行加密
    String code = standardPBEStringEncryptor.encrypt("123456");
    System.out.println(code);
    System.out.println("--------------解密----------------");
    // 解密
    String decrypt = standardPBEStringEncryptor.decrypt(code);
    System.out.println(decrypt);
结果为
 
4、接下来就是启动项目了。
整个配置文件脱敏的配置就算完成了。其实就是两步。配置jasypt的密钥、前缀和后缀等信息;获取加密后的数据(如本文中的数据库密码信息)
脱敏原理
添加BeanFactoryPostProcessor
既然是以springboot方式集成,那么就先从jasypt-spring-boot-starter源码开始入手。该starter中会有一个spring.factories文件,文件中会配置自动装配的类,即JasyptSpringBootAutoConfiguration
 该类又通过Import注解引入了EnableEncryptablePropertiesConfiguration。
其中EnableEncryptablePropertiesConfiguration主要向ioc容器中注入了一个EnableEncryptablePropertiesBeanFactoryPostProcessor,其中一个参数为environment,如下图所示,我们只要把这个类搞明白就理解核心了。
 
该BeanFactoryPostProcessor的目的
那么EnableEncryptablePropertiesBeanFactoryPostProcessor是什么呢?那就要看他实现的接口,该类实现了BeanFactoryPostProcessor接口,那么我们有理由去看postProcessBeanFactory方法(Spring的生命周期知识点)。
 在postProcessBeanFactory方法里,该方法的主要逻辑为获取environment的propSources并进行convert转换,如下图所示。
 
propSources是什么?
propSources其实是环境变量文件或者配置文件的集合。如下图所示。我们直接下标为6的元素里的数据,其实可以发现下标为6的元素对应的就是我们的application.yml。
 
 其实EnableEncryptablePropertiesBeanFactoryPostProcessor获取上述的环境变量文件或者配置文件的数据也可以理解,毕竟你需要对里面的数据进行加密和解密,你不拿到数据怎么加密和解密呢?
convert动作的逻辑是什么?
convert的代码如下图所示,对于propSources里的每一个元素ps,都通过makeEncryptable方法转换为一个新的对象,并替换掉原来的ps。
 
 那么makeEncryptable方法是怎么替换的?一直跟上面的convert最后到了下图中的方法。其实就是把propertySource对象进行包装Wrapper,然后替换掉原来的propertySource对象。
 
 当我们想获取某个配置文件中的kv时,再调用getProperty时其实已经走EncryptablePropertySourceWrapper的getProperty方法了(因为上面被替换了)。
获取配置文件的kv进行过滤和解密
当然经过各种实现类和实现类里依赖的其他接口的实现类,最后getProperty的方法会到下面的方法里,如下面代码所示,其实就是先在原始source获取数据,然后判断是否需要解密,需要的话就直接把解密后的数据返回,完美了。
#EncryptablePropertySource
    default Object getProperty(EncryptablePropertyResolver resolver, EncryptablePropertyFilter filter, PropertySource<T> source, String name) {
        Object value = source.getProperty(name);
        if (filter.shouldInclude(source, name) && value instanceof String) {
            String stringValue = String.valueOf(value);
            return resolver.resolvePropertyValue(stringValue);
        }
        return value;
    }
总结
注册一个BeanFactoryPostProcessor
 在postProcessBeanFactory方法里对propSources进行包装为propSourcesWrapper
 在获取配置文件时对propSourcesWrapper进行获取数据,当符合解密规则时进行解密
参考
Springboot 配置文件、隐私数据脱敏的最佳实践(原理+源码)



















