我的任务是在配置文件中混淆密码。虽然我不认为这是正确的方法,但经理不同意......
所以我正在开发的项目基于Spring Boot,我们正在使用YAML配置文件。目前密码是纯文本的:
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)
为了使其工作,我想使用正则表达式解析属性值,如果匹配则将值替换为deobfuscated / decrypted值。
但我如何截取属性值?
答案 0 :(得分:14)
如果最终让这个工作。 (主要归功于stephane-deraco上的github)
解决方案的关键是实现ApplicationContextInitializer<ConfigurableApplicationContext>
的类。我称之为PropertyPasswordDecodingContextInitializer
。
主要问题是让春天使用这个ApplicationContextInitializer
。重要信息可在reference中找到。我选择使用 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();
}
}
<强> 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相关的机制将无效。
更新:包含@ jny的建议。
答案 1 :(得分:4)
我使用了@ Daniele Torino的答案并做了一些小改动。
首先,由于他链接到如何使弹簧识别初始化器的选项,我选择在Application
中执行此操作:
public static void main(String[] args) throws Exception {
SpringApplication application=new SpringApplication(Application.class);
application.addInitializers(new PropertyPasswordDecodingContextInitializer());
application.run(args);
}
其次,IDEA告诉我,else if (source instanceof CompositePropertySource) {
是多余的,因为CompositePropertySource
继承自EnumerablePropertySource
。
第三,我相信有一个小错误:它弄乱了财产解决的顺序。如果您在环境中有一个编码属性,而在application.properties
文件中有另一个属性,则环境值将被application.properties
值覆盖。
我改变了逻辑,在编码之前插入decodeProperties:
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));
}
}
答案 2 :(得分:4)
答案 3 :(得分:3)
受到@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>
<强> 2。在App.java中(注意:要在tomcat上部署解密的springboot,你应该添加@ServletComponentScan注释并扩展SpringBootServletInitializer)
@SpringBootApplication
@ServletComponentScan
@EnableEncryptableProperties
@PropertySource(name="EncryptedProperties", value = "classpath:config/encrypted.properties")
public class App extends SpringBootServletInitializer {
public static void main(String[] args) throws Exception {
SpringApplication.run(App.class, args);
}
}
第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
输出类似于以下演示:
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
----ENVIRONMENT-----------------
Runtime: Oracle Corporation Java HotSpot(TM) 64-Bit Server VM 25.45-b02
----ARGUMENTS-------------------
algorithm: PBEWithMD5AndDES
input: mypassword
password: mykey
----OUTPUT----------------------
5XNwZF4qoCKTO8M8KUjRprQbivTkmI8H
<强> 4。在目录src / main / resources / config下添加两个属性文件:
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 :(得分:0)
使用Spring Cloud配置服务器
定义crypto.key = MySecretKey
发布消息以加密https://config-server/encrypt
现在就像定义密码一样
app.password={cipher}encryptedvalue
在代码中使用@Value("${app.password}")
并且Spring Boot应该为您提供解密的值