进程Spring引导外部化属性值
我的任务是混淆配置文件中的密码。虽然我认为这不是正确的方法,但经理们不同意 因此,我正在从事的项目是基于Spring Boot的,我们正在使用YAML配置文件。当前密码为纯文本:进程Spring引导外部化属性值,spring,spring-boot,Spring,Spring Boot,我的任务是混淆配置文件中的密码。虽然我认为这不是正确的方法,但经理们不同意 因此,我正在从事的项目是基于Spring Boot的,我们正在使用YAML配置文件。当前密码为纯文本: spring: datasource: url: jdbc:sqlserver://DatabaseServer driverClassName: com.microsoft.sqlserver.jdbc.SQLServerDriver username: ele
spring:
datasource:
url: jdbc:sqlserver://DatabaseServer
driverClassName: com.microsoft.sqlserver.jdbc.SQLServerDriver
username: ele
password: NotTheRealPassword
其想法是使用一些特殊语法来支持模糊或加密密码:
spring:
datasource:
url: jdbc:sqlserver://DatabaseServer
driverClassName: com.microsoft.sqlserver.jdbc.SQLServerDriver
username: ele
password: password(Tm90VGhlUmVhbFBhc3N3b3Jk)
为了实现这一点,我希望使用正则表达式解析属性值,如果它匹配,则将该值替换为deobfouscated/decrypted值
但是如何截取属性值呢?如果最终实现了这一点。(主要归功于on) 解决方案的关键是实现
ApplicationContextInitializer
的类。我称之为PropertyPasswordDecodingContextInitializer
主要问题是让spring使用这个ApplicationContextInitializer
。重要信息可在中找到。我选择了使用META-INF/spring.factories的方法,其中包含以下内容:
org.springframework.context.ApplicationContextInitializer=ch.mycompany.myproject.PropertyPasswordDecodingContextInitializer
PropertyPasswordDecodingContextInitializer
使用一个PropertyPasswordDecoder
和一个实现类,为简单起见,目前使用一个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();
}
}
package ch.mycompany.myproject;
public interface PropertyPasswordDecoder {
public String decodePassword(String encodedPassword);
}
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);
}
}
}
Base64PropertyPasswordDecoder.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();
}
}
package ch.mycompany.myproject;
public interface PropertyPasswordDecoder {
public String decodePassword(String encodedPassword);
}
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相关的机制将无法工作
更新:包含了@的建议。我使用了@的答案,并做了一些小改动 首先,由于他链接到如何使spring识别初始值设定项的选项,我选择在
应用程序中执行此操作:
public static void main(String[] args) throws Exception {
SpringApplication application=new SpringApplication(Application.class);
application.addInitializers(new PropertyPasswordDecodingContextInitializer());
application.run(args);
}
其次,IDEA告诉我,else if(CompositePropertySource的源实例){
是冗余的,这是因为CompositePropertySource
继承自EnumerablePropertySource
第三,我认为有一个小错误:它扰乱了属性解析的顺序。如果您在环境中有一个编码属性,而在应用程序.properties
文件中有另一个编码属性,则环境值将被应用程序.properties
值覆盖。
我更改了逻辑,在编码之前插入decodedProperties:
for (PropertySource<?> propertySource : environment.getPropertySources()) {
Map<String, Object> propertyOverrides = new LinkedHashMap<>();
decodePasswords(propertySource, propertyOverrides);
if (!propertyOverrides.isEmpty()) {
environment.getPropertySources().addBefore(propertySource.getName(), new MapPropertySource("decoded"+propertySource.getName(), propertyOverrides));
}
}
for(PropertySource PropertySource:environment.getPropertySources()){
Map propertyOverrides=新建LinkedHashMap();
解码密码(propertySource、propertyOverrides);
如果(!propertyOverrides.isEmpty()){
environment.getPropertySources().addBefore(propertySource.getName(),新映射propertySource(“decoded”+propertySource.getName(),propertyOverrides));
}
}
只需使用,开箱即用灵感来自@gogstad。以下是我在spring boot项目中的主要操作,加密我的用户名和密码,并在与tomcat合作的项目中解密它们:
1.在pom.xml文件中
<dependency>
<groupId>com.github.ulisesbocchio</groupId>
<artifactId>jasypt-spring-boot</artifactId>
<version>1.12</version>
</dependency>
…
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<targetPath>${project.build.directory}/classes</targetPath>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
</includes>
<targetPath>${project.build.directory}/classes</targetPath>
</resource>
</resources>
…
</build>
3.加密您的用户名和密码,并用结果填写application.properties文件:
java -cp ~/.m2/repository/org/jasypt/jasypt/1.9.2/jasypt-1.9.2.jar org.jasypt.intf.cli.JasyptPBEStringEncryptionCLI input="mypassword" password=mykey algorithm=PBEWithMD5AndDES
a. application.properties
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://xxx
spring.datasource.username=ENC(xxx)
spring.datasource.password=ENC(xxx)
mybatis.mapper-locations=classpath:*/mapper/*.xml
mybatis.type-aliases-package=com.xx.xxx.model
logging.level.com.xx.xxx: DEBUG
b. encrypted.properties
jasypt.encryptor.password=mykey
输出如下图所示:
4.在src/main/resources/config目录下添加两个属性文件:
java -cp ~/.m2/repository/org/jasypt/jasypt/1.9.2/jasypt-1.9.2.jar org.jasypt.intf.cli.JasyptPBEStringEncryptionCLI input="mypassword" password=mykey algorithm=PBEWithMD5AndDES
a. application.properties
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://xxx
spring.datasource.username=ENC(xxx)
spring.datasource.password=ENC(xxx)
mybatis.mapper-locations=classpath:*/mapper/*.xml
mybatis.type-aliases-package=com.xx.xxx.model
logging.level.com.xx.xxx: DEBUG
b. encrypted.properties
jasypt.encryptor.password=mykey
使用spring云配置服务器
定义encrypt.key=MySecretKey
发布要加密的消息
现在定义密码就像
app.password={cipher}encryptedvalue
在代码中使用@Value(“${app.password}”)
spring boot应该会给你解密后的值如果你只是在使用spring内置的东西,我认为你不能。但是,如果你手动加载这个YAML,然后将它输入spring,那么就在那里进行。我想我可以写我自己的PropertySource。我希望找到一种适用于所有属性源的机制。你可能是inte感谢您的链接,看起来很有希望。我明天将尝试一些建议的方法。我注意到您在《为什么是这样?》中使用了apache版本的base64,而不是您的代码。基本上,我并不关心要使用什么实现。我们已经使用了apache commons库,所以我没有注意到spring boot中也有一个。我ave更新了答案以包含您的建议。您的答案似乎有一个小错误,您创建了两个MapPropertySource
——第二个以decodedProperties
为参数。该参数应该是Map
,但却是PropertySource
。1.使用配置服务器是一种范式转换,而不是OP正在寻找问题的解决方案。2.他在寻找Base64编码,而不是加密。OP想要的是编码,而不是加密。