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

Stella981
• 阅读 804

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 允许在应用程序之前操作环境属性值 Spring Boot 实现配置文件加解密原理

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权限管理系统 欢迎关注

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
待兔 待兔
3个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Jacquelyn38 Jacquelyn38
3年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Stella981 Stella981
3年前
KVM调整cpu和内存
一.修改kvm虚拟机的配置1、virsheditcentos7找到“memory”和“vcpu”标签,将<namecentos7</name<uuid2220a6d1a36a4fbb8523e078b3dfe795</uuid
Wesley13 Wesley13
3年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
3年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Stella981 Stella981
3年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
9个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这