springboot通过EnvironmentPostProcessor对配置文件解密

文章来源原创   作者:临窗旋墨   发布时间:2022-03-22   阅读:4113   标签:springboot 分类:springboot 专题:springboot

springboot通过EnvironmentPostProcessor对配置文件解密

把一些密码的明文放在配置文件中毕竟不妥。改为密文,然后在装载到spring的时候解密稍微安全一点。

实现spring加解密的方式有很多,比如jasypt-spring-boot-starter就提供了这样的方式,不过本人觉得这个功能比较简单,不是特别想引入其他依赖,就随手写一个。

本质上和jasypt一样都是对对ConfigurableEnvironment中的配置进行处理,比如jasypt-spring-boot-starter的处理时机是 EnableEncryptablePropertiesBeanFactoryPostProcessor implements BeanFactoryPostProcessor#postProcessBeanFactory,也就是在bean实例化以后但是在调用其初始化方法进行回调以达到对bean进行处理的效果。

(此处提醒使用前后置处理的时候可能会提升某些bean的初始化时机,需要注意)。

源码在此

源码地址: 临窗旋墨/boot

  1. package pers.vic.boot.base.tool.evn;
  2. import org.slf4j.Logger;
  3. import org.slf4j.LoggerFactory;
  4. import org.springframework.boot.SpringApplication;
  5. import org.springframework.boot.env.EnvironmentPostProcessor;
  6. import org.springframework.core.env.ConfigurableEnvironment;
  7. import org.springframework.core.env.MapPropertySource;
  8. import org.springframework.core.env.MutablePropertySources;
  9. import org.springframework.core.env.PropertySource;
  10. import pers.vic.boot.util.ApplicationPropertiesUtil;
  11. import pers.vic.boot.util.encrypt.Base62Enhance;
  12. import java.util.HashMap;
  13. import java.util.Map;
  14. /**
  15. * 描述:配置文件解密后置处理器
  16. * 原理:spring application context refreshed之前定制application运行环境,插入/修改配置信息 <br />
  17. * 另:jasypt-spring-boot-starter的处理时机是 EnableEncryptablePropertiesBeanFactoryPostProcessor implements BeanFactoryPostProcessor#postProcessBeanFactory<br />
  18. * @author Vic.xu
  19. * @date 2022-03-22 9:31
  20. */
  21. public class DecryptEnvironmentPostProcessor implements EnvironmentPostProcessor {
  22. /**
  23. * 配置文件中配置salt的key
  24. */
  25. private static String salt_key = "decrypt.salt";
  26. /**
  27. * 获取配置文件的salt,没有则默认为vic.xu
  28. */
  29. private static String salt = ApplicationPropertiesUtil.getString(salt_key, "vic.xu");
  30. /**
  31. * 加密的属性的前缀:dec() + 密文 eg: dec()passWord
  32. */
  33. private final static String DECRYPT_PREFIX = "dec()";
  34. private final static int DECRYPT_PREFIX_LENGTH = DECRYPT_PREFIX.length();
  35. /**
  36. *创建一个简单的加密实例
  37. */
  38. private static Base62Enhance enhance = Base62Enhance.createInstance(salt);
  39. /**
  40. * 替换过的resource的name
  41. */
  42. private static final String DECRYPT_RESOURCE_NAME = "decryptResource";
  43. private Logger logger = LoggerFactory.getLogger(DecryptEnvironmentPostProcessor.class);
  44. @Override
  45. public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
  46. MutablePropertySources propertySources = environment.getPropertySources();
  47. Map<String, Object> replacedMap = new HashMap<>();
  48. for (PropertySource<?> propertySource : propertySources) {
  49. if (propertySource instanceof MapPropertySource) {
  50. MapPropertySource ps = (MapPropertySource) propertySource;
  51. replace(ps, replacedMap);
  52. }
  53. }
  54. if (!replacedMap.isEmpty()) {
  55. logger.info("{} decrypt properties has replace !", replacedMap.size());
  56. propertySources.addFirst(new MapPropertySource(DECRYPT_RESOURCE_NAME, replacedMap));
  57. }
  58. }
  59. /**
  60. * 找出需要解密的value并解密
  61. * @param ps
  62. * @param replacedMap
  63. */
  64. private void replace(MapPropertySource ps, Map<String, Object> replacedMap) {
  65. for (String name : ps.getPropertyNames()) {
  66. Object value = ps.getProperty(name);
  67. if (value != null) {
  68. String v = String.valueOf(value);
  69. //解密的value以dec()开头
  70. if (v.length() <= DECRYPT_PREFIX_LENGTH || !v.startsWith(DECRYPT_PREFIX)) {
  71. continue;
  72. }
  73. v = v.substring(DECRYPT_PREFIX_LENGTH);
  74. v = enhance.decode(v);
  75. replacedMap.put(name, v);
  76. }
  77. }
  78. }
  79. /**
  80. * 对外提供的加密方法, 和此中解码方法对应
  81. */
  82. public static String enc(String value) {
  83. return enhance.encode(value);
  84. }
  85. }

如上,利用EnvironmentPostProcessor的后置环境处理器,获取到全部的MapPropertySource类型的资源,根据值中是否以指定字符开始,作为依据进行解密,然后放到新建的MapPropertySource中,进而加入MutablePropertySources的开头位置;

使用:META-INF/spring.factories中配置:

  1. org.springframework.boot.env.EnvironmentPostProcessor=pers.vic.boot.base.tool.evn.DecryptEnvironmentPostProcessor

测试略

其他:

  1. 可以考虑使用jasypt-spring-boot-starter,省却自己写代码
  2. 也可以使如jasypt使用#postProcessBeanFactory 后置处理器对spring环境进行处理
  3. 也可以自定义PropertyPlaceholderConfigurer,直接替换属性,原理都类似

结语

在写项目的时候要有一定的安全意识,注意对资源的保护。对于这些重要的资源和中间件(如zk,mysql,redis,nacos等等),建议不要对公网开放,若真的需要开发公网,则注意限定客户端ip,以及软件本身的权限限制(前往不要使用root权限)。

2022-03-22 随手记 by Vic.xu 临窗旋墨


发表评论

目录