处理 Spring Boot 外化属性值 [英] Process Spring Boot externalized property values
问题描述
我的任务是在我们的配置文件中混淆密码.虽然我认为这不是正确的方法,但管理人员不同意...
I have the task of obfuscating passwords in our configuration files. While I don't think this is the right approach, managers disagree...
所以我正在做的项目是基于 Spring Boot 的,我们使用的是 YAML 配置文件.目前密码为纯文本:
So the project I am working on is based on Spring Boot and we are using YAML configuration files. Currently the passwords are in plain text:
spring:
datasource:
url: jdbc:sqlserver://DatabaseServer
driverClassName: com.microsoft.sqlserver.jdbc.SQLServerDriver
username: ele
password: NotTheRealPassword
这个想法是有一些特殊的语法来支持混淆或加密的密码:
The idea is to have some special syntax that supports an obfuscated or encrypted password:
spring:
datasource:
url: jdbc:sqlserver://DatabaseServer
driverClassName: com.microsoft.sqlserver.jdbc.SQLServerDriver
username: ele
password: password(Tm90VGhlUmVhbFBhc3N3b3Jk)
为了使其工作,我想使用正则表达式解析属性值,如果匹配,则用反混淆/解密的值替换该值.
In order for this to work I want to parse the property values using a regular expression and if it matches replace the value with the deobfuscated/decrypted value.
但是我如何截取属性值?
推荐答案
如果最终让这个工作.(主要感谢 stephane-deraco 在 github)
If finally got this to work. (Mainly thanks to stephane-deraco on github)
解决方案的关键是一个实现ApplicationContextInitializer
的类.我称之为PropertyPasswordDecodingContextInitializer
.
Key to the solution is a class that implements ApplicationContextInitializer<ConfigurableApplicationContext>
. I called it PropertyPasswordDecodingContextInitializer
.
主要问题是让 spring 使用这个 ApplicationContextInitializer
.重要信息可以在 参考.我选择了使用具有以下内容的 META-INF/spring.factories 的方法:
The main problem was to get spring to use this ApplicationContextInitializer
. Important information can be found in the reference. I chose the approach using a META-INF/spring.factories with following content:
org.springframework.context.ApplicationContextInitializer=ch.mycompany.myproject.PropertyPasswordDecodingContextInitializer
PropertyPasswordDecodingContextInitializer
使用一个 PropertyPasswordDecoder
和一个实现类,目前为简单起见是 Base64PropertyPasswordDecoder
.
The PropertyPasswordDecodingContextInitializer
uses a PropertyPasswordDecoder
and an implementing class, currently for simplicity a Base64PropertyPasswordDecoder
.
PropertyPasswordDecodingContextInitializer.java
package ch.mycompany.myproject;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.env.CompositePropertySource;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.EnumerablePropertySource;
import org.springframework.core.env.MapPropertySource;
import org.springframework.core.env.PropertySource;
import org.springframework.stereotype.Component;
@Component
public class PropertyPasswordDecodingContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
private static final Pattern decodePasswordPattern = Pattern.compile("password\((.*?)\)");
private PropertyPasswordDecoder passwordDecoder = new Base64PropertyPasswordDecoder();
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
ConfigurableEnvironment environment = applicationContext.getEnvironment();
for (PropertySource<?> propertySource : environment.getPropertySources()) {
Map<String, Object> propertyOverrides = new LinkedHashMap<>();
decodePasswords(propertySource, propertyOverrides);
if (!propertyOverrides.isEmpty()) {
PropertySource<?> decodedProperties = new MapPropertySource("decoded "+ propertySource.getName(), propertyOverrides);
environment.getPropertySources().addBefore(propertySource.getName(), decodedProperties);
}
}
}
private void decodePasswords(PropertySource<?> source, Map<String, Object> propertyOverrides) {
if (source instanceof EnumerablePropertySource) {
EnumerablePropertySource<?> enumerablePropertySource = (EnumerablePropertySource<?>) source;
for (String key : enumerablePropertySource.getPropertyNames()) {
Object rawValue = source.getProperty(key);
if (rawValue instanceof String) {
String decodedValue = decodePasswordsInString((String) rawValue);
propertyOverrides.put(key, decodedValue);
}
}
}
}
private String decodePasswordsInString(String input) {
if (input == null) return null;
StringBuffer output = new StringBuffer();
Matcher matcher = decodePasswordPattern.matcher(input);
while (matcher.find()) {
String replacement = passwordDecoder.decodePassword(matcher.group(1));
matcher.appendReplacement(output, replacement);
}
matcher.appendTail(output);
return output.toString();
}
}
PropertyPasswordDecoder.java
package ch.mycompany.myproject;
public interface PropertyPasswordDecoder {
public String decodePassword(String encodedPassword);
}
Base64PropertyPasswordDecoder.java
package ch.mycompany.myproject;
import java.io.UnsupportedEncodingException;
import org.apache.commons.codec.binary.Base64;
public class Base64PropertyPasswordDecoder implements PropertyPasswordDecoder {
@Override
public String decodePassword(String encodedPassword) {
try {
byte[] decodedData = Base64.decodeBase64(encodedPassword);
String decodedString = new String(decodedData, "UTF-8");
return decodedString;
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
}
}
请注意,ApplicationContext 在此阶段尚未完成初始化,因此自动装配或任何其他与 bean 相关的机制都不起作用.
Mind you, the ApplicationContext has not finished initialized at this stage, so autowiring or any other bean related mechanisms won't work.
更新:包括@jny 的建议.
这篇关于处理 Spring Boot 外化属性值的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!