处理Spring Boot外部化属性值

时间:2015-08-13 13:38:36

标签: spring spring-boot

我的任务是在配置文件中混淆密码。虽然我不认为这是正确的方法,但经理不同意......

所以我正在开发的项目基于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值。

但我如何截取属性值?

5 个答案:

答案 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应该为您提供解密的值