Если, наконец, это получилось. (В основном благодаря stephane-deraco по адресу github)
Ключом к решению является класс, который реализует ApplicationContextInitializer<ConfigurableApplicationContext>
. Я назвал его PropertyPasswordDecodingContextInitializer
.
Основная проблема заключалась в том, чтобы получить весну для использования этого ApplicationContextInitializer
. Важную информацию можно найти в reference. Я выбрал подход с использованием МЕТА-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 не завершил инициализацию на этом этапе, поэтому автоустановка или любые другие связанные с ним механизмы не будут работать.
Обновление: Включено @jny «s предложения.
Я не думаю, что вы можете, если вы просто используете что-то, встроенное в Весну. Однако, если вы загружаете этот YAML вручную, а затем загружаете его в Spring, делайте это там. –
Я полагаю, что я мог бы написать свой собственный PropertySource. Я надеялся найти механизм, который применяется ко всем источникам собственности. –
Возможно, вас заинтересует эта проблема с Spring Boot (https://github.com/spring-projects/spring-boot/issues/1312) –