专栏首页冷冷Spring Boot 实现配置文件加解密原理

Spring Boot 实现配置文件加解密原理

背景

接上文《失踪人口回归,mybatis-plus 3.3.2 发布》[1] ,提供了一个非常实用的功能 「数据安全保护」 功能,不仅支持数据源的配置加密,对于 spring boot 全局的 yml /properties 文件均可实现敏感信息加密功能,在一定的程度上控制开发人员流动导致敏感信息泄露。

// 数据源敏感信息加密

spring:
  datasource:
    url: mpw:qRhvCwF4GOqjessEB3G+a5okP+uXXr96wcucn2Pev6BfaoEMZ1gVpPPhdDmjQqoM
    password: mpw:Hzy5iliJbwDHhjLs1L0j6w==
    username: mpw:Xb+EgsyuYRXw7U7sBJjBpA==

// 数据源敏感信息加密

spring:
  redis:
    password: mpw:Hzy5iliJbwDHhjLs1L0j6w==

实现原理

我们翻开 spring boot 官方文档,翻到 4.2.6 章节 Spring Boot 不提供对加密属性值的任何内置支持,但是提供修改 Spring 环境中包含的值所必需的扩展点 EnvironmentPostProcessor 允许在应用程序之前操作环境属性值

mybatis-plus 的实现

public class SafetyEncryptProcessor implements EnvironmentPostProcessor {

 @Override
 public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
  //命令行中获取密钥
  String mpwKey = null;

  // 返回全部形式的配置源(环境变量、命令行参数、配置文件 ...)
  for (PropertySource<?> ps : environment.getPropertySources()) {
   // 判断是否需要含有加密密码,没有就直接跳过
   if (ps instanceof SimpleCommandLinePropertySource) {
    SimpleCommandLinePropertySource source = (SimpleCommandLinePropertySource) ps;
    mpwKey = source.getProperty("mpw.key");
    break;
   }
  }

  //处理加密内容(获取到原有配置,然后解密放到新的map 里面(key是原有key))
  HashMap<String, Object> map = new HashMap<>();
  for (PropertySource<?> ps : environment.getPropertySources()) {
   if (ps instanceof OriginTrackedMapPropertySource) {
    OriginTrackedMapPropertySource source = (OriginTrackedMapPropertySource) ps;
    for (String name : source.getPropertyNames()) {
     Object value = source.getProperty(name);
     if (value instanceof String) {
      String str = (String) value;
      if (str.startsWith("mpw:")) {
       map.put(name, AES.decrypt(str.substring(4), mpwKey));
      }
     }
    }
   }
  }
  // 将解密的数据放入环境变量,并处于第一优先级上 (这里一定要注意,覆盖其他配置)
  if (!map.isEmpty()) {
   environment.getPropertySources().addFirst(new MapPropertySource("custom-encrypt", map));
  }
 }
}

如何加载生效

resources/META-INF/spring.factories 配置 SPI

org.springframework.boot.env.EnvironmentPostProcessor=\
  com.baomidou.mybatisplus.autoconfigure.SafetyEncryptProcessor

扩展

mybatis-plus 默认是读取启动参数,可以在此处可以根据自己需求修改为更安全的根密钥存储。

读取环境变量

System.getProperty("mpw.key")

远程加载密码服务

// 此处思路,参考 druid ConfigFilter
public Properties loadConfig(String filePath) {
      Properties properties = new Properties();

      InputStream inStream = null;
      try {
          boolean xml = false;
          if (filePath.startsWith("file://")) {
              filePath = filePath.substring("file://".length());
              inStream = getFileAsStream(filePath);
              xml = filePath.endsWith(".xml");
          } else if (filePath.startsWith("http://") || filePath.startsWith("https://")) {
              URL url = new URL(filePath);
              inStream = url.openStream();
              xml = url.getPath().endsWith(".xml");
          } else if (filePath.startsWith("classpath:")) {
              String resourcePath = filePath.substring("classpath:".length());
              inStream = Thread.currentThread().getContextClassLoader().getResourceAsStream(resourcePath);
              // 在classpath下应该也可以配置xml文件吧?
              xml = resourcePath.endsWith(".xml");
          } else {
              inStream = getFileAsStream(filePath);
              xml = filePath.endsWith(".xml");
          }

          if (inStream == null) {
              LOG.error("load config file error, file : " + filePath);
              return null;
          }

          if (xml) {
              properties.loadFromXML(inStream);
          } else {
              properties.load(inStream);
          }

          return properties;
      } catch (Exception ex) {
          LOG.error("load config file error, file : " + filePath, ex);
          return null;
      } finally {
          JdbcUtils.close(inStream);
      }
  }

总结

  • 配置文件加解密,是通过自定义扩展 EnvironmentPostProcessor 实现
  • 若项目中没有使用最新版本 mybatis-plus ,可以参考如上自己实现,不过我推荐 jasypt-spring-boot-starter[2] ,原理类似实现了一个 EnableEncryptablePropertySourcesPostProcessor ,但是支持的加密方式更多更成熟
  • 关于 jasypt 使用可以参考源码: https://gitee.com/log4j/pig

项目推荐: Spring Cloud 、Spring Security OAuth2的RBAC权限管理系统 欢迎关注

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Spring Security OAuth2 实现登录互踢

    一个账号只能一处登录,类似的业务需求在现有后管类系统是非常常见的。 但在原有的 spring security oauth2 令牌方法流程(所谓的登录)无法满足...

    冷冷
  • 【小技巧】spring security oauth2 令牌实现多终端登录状态同步

    解决不同客户端使用token,各个客户端的登录状态必须保持一致,退出状态实现一致。同上述问题类似如何解决不同租户相同用户名的人员的登录状态问题。

    冷冷
  • 【jfinal】扩展JFIANL 支持加载jar包中SQL模板

    扩展 ActiveRecordPlugin 支持加载jar包中SQL模板 jfinal3.1 中新增了IStringSource ,这个接口方便大家扩展SQL...

    冷冷
  • React-Native 遇到的错误1. React-Native 部分组件在debug模式下打包在iOS真机上可以显示,但是release模式下打包在iOS真机上不显示2. React-Native

    这段代码在release包的情况是,buttons是空的,是由于if (child.type.name === 'FlowSendButton')这是判断根本不...

    贺贺V5
  • Android基于TCP的五子棋双人对战实现

    张风捷特烈
  • Mac应用程序“XXX”不能打开?一条命令就搞定!

    很多人刚从熟悉的Windows转到较为陌生的Mac,在使用过程中遇到一些困难是必然的。有小伙伴就遇到了这样的问题,在网上下载软件,下载下来的软件安装包是经过压缩...

    MAC先森
  • 聊聊flink 1.11 中的随机数据生成器-DataGen connector

    在flink 1.11中,内置提供了一个DataGen 连接器,主要是用于生成一些随机数,用于在没有数据源的时候,进行流任务的测试以及性能测试等。下面我们简单的...

    大数据技术与应用实战
  • Mac OS Sierra如何打开任何来源

      我们知道在Mac升级到最新的Mac OS Sierra系统之后,随之而来的是第三方应用都无法打开,提示的是无法打开或扔进废纸篓。而在之前的版本系统中,我们知...

    mukekeheart
  • SQL---DATE_ADD()函数

    现在,我们希望向 "OrderDate" 添加 2 天,这样就可以找到付款日期。

    IT云清
  • MYSQL向日期添加指定的时间间隔

    week

扫码关注云+社区

领取腾讯云代金券