Skip to content

Instantly share code, notes, and snippets.

@JanisZhang
Last active December 6, 2024 14:08
Show Gist options
  • Save JanisZhang/d908ad3849fe59f1e0fffdad4bc9f08f to your computer and use it in GitHub Desktop.
Save JanisZhang/d908ad3849fe59f1e0fffdad4bc9f08f to your computer and use it in GitHub Desktop.
Spring 应用中,通过实现自定义的解密逻辑来解密配置文件中某些特殊格式的内容
在 Spring 应用中,可以通过实现自定义的解密逻辑来解密配置文件中某些特殊格式的内容。例如,配置文件中的加密内容以 ENC(...) 的形式表示,运行时需要将其解密成原始字符串。以下是实现步骤:
1. 定义一个解密工具类
首先定义一个工具类,用于解密特定格式的字符串:
import org.springframework.stereotype.Component;
@Component
public class Decryptor {
public String decrypt(String encryptedText) {
// 解密逻辑,例如:AES 解密或 Base64 解码
if (encryptedText.startsWith("ENC(") && encryptedText.endsWith(")")) {
String actualEncrypted = encryptedText.substring(4, encryptedText.length() - 1);
// 这里替换为实际的解密方法
return "解密后的内容: " + actualEncrypted;
}
return encryptedText;
}
}
2. 自定义 PropertySource
通过自定义 PropertySource 拦截读取配置文件的过程,对配置值进行解密。
import org.springframework.core.env.EnumerablePropertySource;
import org.springframework.core.env.PropertySource;
public class EncryptedPropertySource extends PropertySource<PropertySource<?>> {
private final Decryptor decryptor;
public EncryptedPropertySource(String name, PropertySource<?> source, Decryptor decryptor) {
super(name, source);
this.decryptor = decryptor;
}
@Override
public Object getProperty(String name) {
Object value = source.getProperty(name);
if (value instanceof String) {
return decryptor.decrypt((String) value);
}
return value;
}
}
3. 配置解密逻辑
使用 EnvironmentPostProcessor 在 Spring 启动时将自定义 PropertySource 注入到 Environment 中。
import org.springframework.boot.SpringApplication;
import org.springframework.boot.env.EnvironmentPostProcessor;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.MutablePropertySources;
import org.springframework.core.env.PropertySource;
public class EncryptedPropertyProcessor implements EnvironmentPostProcessor {
private final Decryptor decryptor = new Decryptor();
@Override
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
MutablePropertySources propertySources = environment.getPropertySources();
for (PropertySource<?> source : propertySources) {
if (source.getName().contains("applicationConfig")) {
propertySources.replace(source.getName(), new EncryptedPropertySource(source.getName(), source, decryptor));
}
}
}
}
在 resources/META-INF/spring.factories 文件中注册 EnvironmentPostProcessor:
org.springframework.boot.env.EnvironmentPostProcessor=com.example.EncryptedPropertyProcessor
4. 配置文件示例
在 application.yml 中,将需要加密的内容用特殊标记表示,例如:
example:
username: admin
password: ENC(ENCRYPTED_PASSWORD)
5. 运行与验证
运行程序时,配置文件中的 ENC(...) 格式内容将会被解密工具类自动处理并转换为明文。
通过这种方式,可以方便地在 Spring 应用中读取并解密配置文件中的敏感数据,同时保持对现有逻辑的最小侵入性。
---------
---------
import org.springframework.boot.SpringApplication;
import org.springframework.boot.env.EnvironmentPostProcessor;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.MutablePropertySources;
import org.springframework.core.env.PropertySource;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.ResourcePropertySource;
import org.springframework.boot.env.YamlPropertySourceLoader;
import java.io.IOException;
public class EncryptedPropertyProcessor implements EnvironmentPostProcessor {
private final Decryptor decryptor = new Decryptor(); // 自定义解密类
@Override
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
String activeProfile = environment.getProperty("spring.profiles.active", "default");
String fileName = activeProfile.equals("default") ? "application.yml" : "application-" + activeProfile + ".yml";
// 加载配置文件
Resource resource = new ClassPathResource("config/" + fileName); // 假设文件在 resources/config 下
if (resource.exists()) {
try {
// 使用 YamlPropertySourceLoader 手动加载配置
YamlPropertySourceLoader loader = new YamlPropertySourceLoader();
PropertySource<?> yamlProperties = loader.load(fileName, resource).get(0);
// 获取 PropertySources 并添加加载的配置
MutablePropertySources propertySources = environment.getPropertySources();
propertySources.addLast(yamlProperties);
// 替换为带解密逻辑的 PropertySource
propertySources.replace(yamlProperties.getName(), new EncryptedPropertySource(yamlProperties.getName(), yamlProperties, decryptor));
} catch (IOException e) {
throw new RuntimeException("Failed to load configuration file: " + fileName, e);
}
} else {
System.out.println("Configuration file not found: " + fileName);
}
}
}
获取激活的 Profile
使用 environment.getProperty("spring.profiles.active", "default") 获取当前激活的 Profile。
根据 Profile 拼接文件名(如 application.yml 或 application-dev.yml)。
加载 YAML 文件
使用 YamlPropertySourceLoader 加载 .yml 文件。
这里假设文件存放在 resources/config 目录下,路径为 config/<fileName>。
将加载的配置添加到 PropertySources
手动加载的配置文件被转化为 PropertySource,可以通过 propertySources.addLast 添加到环境中。
替换为解密逻辑
将加载的配置文件替换为自定义的 EncryptedPropertySource,以支持加密字段的自动解密。
--------
---------
import org.springframework.boot.SpringApplication;
import org.springframework.boot.env.EnvironmentPostProcessor;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.MutablePropertySources;
import org.springframework.core.env.PropertySource;
import org.springframework.core.io.ClassPathResource;
import org.springframework.boot.env.YamlPropertySourceLoader;
import java.io.IOException;
public class EncryptedPropertyProcessor implements EnvironmentPostProcessor {
private final Decryptor decryptor = new Decryptor(); // 自定义解密工具
@Override
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
// Step 1: 手动加载 application.yml 并解析 spring.profiles.active
String activeProfile = loadActiveProfileFromApplicationYaml();
// 如果解析不到 active profile,使用默认值
if (activeProfile == null) {
activeProfile = "default";
}
System.out.println("Active profile detected: " + activeProfile);
// Step 2: 手动加载 profile-specific 配置文件
String fileName = activeProfile.equals("default") ? "application.yml" : "application-" + activeProfile + ".yml";
loadAndProcessYaml(environment, fileName);
}
private String loadActiveProfileFromApplicationYaml() {
try {
// 手动加载 application.yml
YamlPropertySourceLoader loader = new YamlPropertySourceLoader();
ClassPathResource resource = new ClassPathResource("application.yml");
if (resource.exists()) {
PropertySource<?> yamlProperties = loader.load("application.yml", resource).get(0);
// 从配置中解析 spring.profiles.active
return (String) yamlProperties.getProperty("spring.profiles.active");
}
} catch (IOException e) {
throw new RuntimeException("Failed to load application.yml", e);
}
return null;
}
private void loadAndProcessYaml(ConfigurableEnvironment environment, String fileName) {
try {
// 手动加载指定的 YAML 文件
YamlPropertySourceLoader loader = new YamlPropertySourceLoader();
ClassPathResource resource = new ClassPathResource("config/" + fileName);
if (resource.exists()) {
PropertySource<?> yamlProperties = loader.load(fileName, resource).get(0);
// 获取 PropertySources 并添加到环境中
MutablePropertySources propertySources = environment.getPropertySources();
propertySources.addLast(yamlProperties);
// 替换为带解密逻辑的 PropertySource
propertySources.replace(yamlProperties.getName(), new EncryptedPropertySource(yamlProperties.getName(), yamlProperties, decryptor));
} else {
System.out.println("Configuration file not found: " + fileName);
}
} catch (IOException e) {
throw new RuntimeException("Failed to load configuration file: " + fileName, e);
}
}
}
------------
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.MutablePropertySources;
import org.springframework.core.env.PropertySource;
import org.springframework.stereotype.Component;
@Component
public class ConfigurationUpdater {
@Autowired
private ConfigurableEnvironment environment;
public void updatePropertySource() {
// 解密逻辑(假设 decrypt() 方法已经定义好)
String decryptedValue = decrypt("encrypted_value");
// 获取当前的 PropertySource
MutablePropertySources propertySources = environment.getPropertySources();
// 假设你要更新 application-local.yml 中的一个属性
String targetPropertyKey = "my.property"; // 你要更新的属性键
PropertySource<?> originalSource = propertySources.get("applicationConfig: [classpath:/application-local.yml]");
if (originalSource != null) {
// 在解密后创建一个新的 PropertySource,并替换其中的值
propertySources.replace("applicationConfig: [classpath:/application-local.yml]", new PropertySource<Object>("modifiedProperties") {
@Override
public Object getProperty(String name) {
if (targetPropertyKey.equals(name)) {
return decryptedValue; // 替换为解密后的值
}
return originalSource.getProperty(name);
}
});
}
}
private String decrypt(String encryptedValue) {
// 你的解密逻辑
return "decrypted_value";
}
}
------------- 貌似可用 --------------- 貌似可用 ------------------- 貌似可用 -------------------------- 貌似可用 -----------------
package org.example.my;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.env.EnvironmentPostProcessor;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.MutablePropertySources;
import org.springframework.core.env.PropertySource;
import java.util.HashMap;
import java.util.Map;
public class EncryptedPropertyProcessor implements EnvironmentPostProcessor {
@Override
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
String activeProfile = environment.getProperty("spring.profiles.active", "default");
String targetFileName = activeProfile.equals("default") ? "application.yml" : "application-" + activeProfile + ".yml";
MutablePropertySources propertySources = environment.getPropertySources();
PropertySource<?> targetSource = null;
// 动态匹配 PropertySource 名称,找到对应的配置文件
for (PropertySource<?> source : propertySources) {
if (source.getName().contains(targetFileName)) {
targetSource = source;
break;
}
}
if (targetSource != null) {
System.out.println("Found target PropertySource: " + targetSource.getName());
// 将属性解密并存储到新的 Map 中
Map<String, Object> modifiedProperties = new HashMap<>();
for (String key : ((Map<String, Object>) targetSource.getSource()).keySet()) {
Object value = targetSource.getProperty(key);
if ("example.password".equals(key)) {
// 解密并替换
value = decrypt(value);
}
modifiedProperties.put(key, value);
}
// 创建一个新的 PropertySource 并替换
PropertySource<?> modifiedSource = new PropertySource<Object>(
"modified-" + targetFileName, modifiedProperties) {
@Override
public Object getProperty(String name) {
return modifiedProperties.get(name);
}
};
propertySources.replace(targetSource.getName(), modifiedSource);
System.out.println("Successfully replaced PropertySource: " + targetSource.getName());
// 获取动态修改后的属性值
String password = environment.getProperty("example.password");
System.out.println(password);
} else {
System.out.println("PropertySource not found for file: " + targetFileName);
}
}
// 模拟解密逻辑
private Object decrypt(Object encryptedValue) {
return "66666666";
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment