最近在做项目的过程中,PSS提出配置文件中类似数据库连接需要的用户名、密码等敏感信息需要加密处理(之前一直是明文的)。

为了快速完成任务,网上搜刮到jasypt包,也有相应的starter,使用方法可以参考blog

但是还是想具体弄清楚背后的实现。偶然看到Spring Boot中有个EnvironmentPostProcessor接口。看名字,它的实现类应该在配置文件加载完和Spring容器开始初始化之前起作用。这样的话,我们就可以实现该接口用来定制化配置信息,包括解密。

话不多说,show code,

 @Component
public class DecryptAESConfigProcessor implements EnvironmentPostProcessor { private static short INDEX = 0;
private static String ITEM_FORMAT = "spring.config.decrypt-items[%d]";
private static Pattern PATTERN = Pattern.compile("AES\\((.+)\\)");
private static StringBuffer SB = new StringBuffer(); @Override
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
MutablePropertySources propertySources = environment.getPropertySources();
for (PropertySource propertySource: propertySources) {
if (propertySource instanceof OriginTrackedMapPropertySource){
INDEX = 0;
OriginTrackedMapPropertySource otmps = (OriginTrackedMapPropertySource)propertySource;
//System.out.println("property name = " + otmps.getName());
Map<String, Object> source = otmps.getSource();
String secretSalt = source.getOrDefault("spring.config.secret-salt", "").toString();
if (!"".equals(secretSalt)){
String salt = CommonUtil.decrypt(secretSalt, "sns");
while (INDEX > -1){
String item = String.format(ITEM_FORMAT, INDEX);
if (source.containsKey(item)){
String itemValue = source.get(item).toString();
String propertyValue = source.getOrDefault(itemValue, "").toString();
Matcher matcher = PATTERN.matcher(propertyValue);
boolean findAES = false;
while (matcher.find()){
//decrypt each AES()
findAES = true;
String decryptStr = CommonUtil.decrypt(matcher.group(1), salt);
matcher.appendReplacement(SB, decryptStr);
}
if (!findAES){
//decrypt entire item
source.put(itemValue, CommonUtil.decrypt(propertyValue, salt));
} else {
matcher.appendTail(SB);
source.put(itemValue, SB.toString());
}
SB.delete(0, SB.length());
INDEX++;
} else {
INDEX = -1;
}
}
}
}
} }
} 注意:需要将DecryptAESConfigProcessor声明到spring.factories中。 org.springframework.boot.env.EnvironmentPostProcessor=\
org.chris.springboot.config_encrypt.config.DecryptAESConfigProcessor

  

public class CommonUtil {

    private static final String KEY_ALGORITHM = "AES";
private static final String DEFAULT_CIPHER_ALGORITHM = "AES/ECB/PKCS5Padding";
private static Cipher CIPHER;
private static KeyGenerator KEY_GENERATOR; static {
try {
CIPHER = Cipher.getInstance(DEFAULT_CIPHER_ALGORITHM);
KEY_GENERATOR = KeyGenerator.getInstance(KEY_ALGORITHM);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
}
} /**
* Decrypt by AES
* @param content
* @param salt
* @return
*/
public static String decrypt(String content, String salt) {
if (Objects.nonNull(content)) {
try {
byte[] decrypted = Base64.getDecoder().decode(content.getBytes("UTF-8"));
CIPHER.init(Cipher.DECRYPT_MODE, getSecretKey(salt));
return new String(CIPHER.doFinal(decrypted));
} catch (Exception e) {
e.printStackTrace();
}
}
return null;
} /**
* Encrypt by AES
* @param content
* @param salt
* @return
*/
public static String encrypt(String content, String salt) {
if (Objects.nonNull(content)) {
try {
CIPHER.init(Cipher.ENCRYPT_MODE, getSecretKey(salt));
byte[] encrypted = CIPHER.doFinal(content.getBytes("UTF-8"));
return Base64.getEncoder().encodeToString(encrypted);
} catch (Exception e) {
e.printStackTrace();
}
}
return null;
} /**
* Generate encrypted salt
* @param salt
* @return
*/
private static SecretKeySpec getSecretKey(final String salt) {
KEY_GENERATOR.init(128, new SecureRandom(salt.getBytes()));
SecretKey secretKey = KEY_GENERATOR.generateKey();
return new SecretKeySpec(secretKey.getEncoded(), KEY_ALGORITHM);
}
}

最后,配置文件中需要有类似以下配置项

解密规则如下,

1. 采用AES加密解密;

2. 在配置文件中

 (1) 通过spring.config.decrypt-items指定需要解密的配置

 (2) 通过spring.config.secret-salt指定AES的key(最好加密)

3. 如果需要解密的配置项中存在AES()模式的字符串,将会解密 () 中的内容,否则解密整个配置项

Spring Boot - 配置信息后处理的更多相关文章

  1. Redis篇之操作、lettuce客户端、Spring集成以及Spring Boot配置

    Redis篇之操作.lettuce客户端.Spring集成以及Spring Boot配置 目录 一.Redis简介 1.1 数据结构的操作 1.2 重要概念分析 二.Redis客户端 2.1 简介 2 ...

  2. Spring Boot 配置优先级顺序

    一般在一个项目中,总是会有好多个环境.比如: 开发环境 -> 测试环境 -> 预发布环境 -> 生产环境 每个环境上的配置文件总是不一样的,甚至开发环境中每个开发者的环境可能也会有一 ...

  3. spring boot配置springMVC拦截器

    spring boot通过配置springMVC拦截器 配置拦截器比较简单, spring boot配置拦截器, 重写preHandle方法. 1.配置拦截器: 2重写方法 这样就实现了拦截器. 其中 ...

  4. spring boot配置mybatis和事务管理

    spring boot配置mybatis和事务管理 一.spring boot与mybatis的配置 1.首先,spring boot 配置mybatis需要的全部依赖如下: <!-- Spri ...

  5. Spring Boot 配置元数据指南

    1. 概览 在编写 Spring Boot 应用程序时,将配置属性映射到 Java bean 上是非常有用的.但是,记录这些属性的最好方法是什么呢? 在本教程中,我们将探讨 Spring Boot C ...

  6. spring boot 配置虚拟静态资源文件

    我们实现的目的是:通过spring boot 配置静态资源访问的虚拟路径,可实现在服务器,或者在本地通过:http://ip地址:端口/资源路径/文件名  ,可直接访问文件 比如:我们本地电脑的:E: ...

  7. Spring Boot配置,读取配置文件

    Spring Boot配置,读取配置文件 一.配置Spring Boot 1.1 服务器配置 1.2 使用其他Web服务器 1.3 配置启动信息 1.4 配置浏览器显示ico 1.5 Yaml语法 1 ...

  8. Spring Boot -- 配置切换指南

    一般在一个项目中,总是会有好多个环境.比如: 开发环境 -> 测试环境 -> 预发布环境 -> 生产环境 每个环境上的配置文件总是不一样的,甚至开发环境中每个开发者的环境可能也会有一 ...

  9. spring boot 配置注入

    spring boot配置注入有变量方式和类方式(参见:<spring boot 自定义配置属性的各种方式>),变量中又要注意静态变量的注入(参见:spring boot 给静态变量注入值 ...

随机推荐

  1. Frequency

    题目描述 Snuke loves constructing integer sequences. There are N piles of stones, numbered 1 through N. ...

  2. [POI2014]Cards

    题目大意: 有$n(n\le2\times10^5)$张卡片排成一排,每张卡片正反面有两个数$a_i$和$b_i$.$m(m\le10^6)$次操作,每次交换第$c_i$和第$d_i$张卡片,问若可以 ...

  3. 2.2多线程(java学习笔记)线程状态及线程操作的相关方法

    一.线程的状态 线程一般具有五种状态,即创建.就绪.运行.阻塞.终止. 它们之间的关系: 二.线程操作相关方法 1.设置和取得线程名称. 如果不设置线程名称,系统会自动分配线程名,一般格式为Threa ...

  4. [置顶] kubernetes资源对象--ConfigMap

    原理 很多生产环境中的应用程序配置较为复杂,可能需要多个config文件.命令行参数和环境变量的组合.使用容器部署时,把配置应该从应用程序镜像中解耦出来,以保证镜像的可移植性.尽管Secret允许类似 ...

  5. JDBCUtils工具类

    JdbcUtils.java import java.sql.Connection; import java.sql.SQLException; import javax.sql.DataSource ...

  6. Qt creator发布可执行文件方式----靠谱

    1.首先用 QtCreator 新建一个 Qt Widgets Application 项目,直接用默认的 QMainWindow 程序就可以了,项目名字假定是serial_port.exe. 然后以 ...

  7. django前后端数据传输学习记录

    在开发过程中会遇到这样的情况 后台返回了一堆的数据,是一个列表 例如 datas = [{"a":1, "b":2}, {"c": 3,&q ...

  8. ES里关于对象的拓展

    一.对象类别 在浏览器这样的执行环境中,对象没有统一的标准,在标准中又使用不同的术语描述对象,ES6规范清晰定义了每一个类别的对象,对象的类别如下 1.普通(Ordinary)对象:具有JS对象所有的 ...

  9. redis-cli使用密码登录

    redis-cli使用密码登录 注意IP地址要写正确! 学习了: https://blog.csdn.net/lsm135/article/details/52932896 https://blog. ...

  10. Spring声明式事务的配置方式

    1.事务的特性   原子性:事务中的操作是不可分割的一部分   一致性:要么同时成功,要么同时失败(事务执行前后数据保持一致)   隔离性:并发互不干扰     持久性:事务一旦被提交,它就是一条持久 ...