Process Spring Boot externalized property values

后端 未结 5 719
广开言路
广开言路 2020-12-03 12:22

I have the task of obfuscating passwords in our configuration files. While I don\'t think this is the right approach, managers disagree...

So the project I am workin

5条回答
  •  生来不讨喜
    2020-12-03 13:11

    If finally got this to work. (Mainly thanks to stephane-deraco on github)

    Key to the solution is a class that implements ApplicationContextInitializer. I called it PropertyPasswordDecodingContextInitializer.

    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
    

    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 {
    
        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 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 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);
            }
        }
    
    
    }
    

    Mind you, the ApplicationContext has not finished initialized at this stage, so autowiring or any other bean related mechanisms won't work.


    Update: Included @jny's suggestions.

提交回复
热议问题