We have mix of some legacy spring apps which are not yet migrated to spring-boot or spring cloud and also spring boot apps. I am working on creating a Spring component that will automatically decrypt spring properties when the environment is loaded if the property value is encrypted and has a prefix. The properties can be in .properties files(for legacy apps) or in .yaml files(newer spring boot apps).
The component should be able to decrypt any spring property regardless of the source, and should work with any spring version and not tied to spring boot.The component should also transparently decrypt properties. It should read passphrase from a property file, so the passphrase file needs to be loaded in the beginning.
We have our own ecrypt/decrypt and don't want to use jaspyt.
Things tried so far:
I liked this approach of creating an ApplicationListener, but this is tied up with spring boot(ApplicationEnvironmentPreparedEvent). With Spring events like ContextRefreshed or ContextStart , i don't see how can i get ConfigurableApplicationContext/ConfigurableEnvironment. Anyone created a Listener for encrypt/decrypt withouth spring boot/cloud?
I also created a custom ApplicationContextInitializer, and added it to web.xml's context-param, but this doesn't seems to be working. When i debug into it, i don't think it is loading/reading properties from my app.properties file.
@Component
public class DecryptingPropertyContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
@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 ) {
//decrypt logic here
propertyOverrides.put( key, decryptedValue );
}
}
}
}
}
Does anyone had to do something similar or has any better ideas ? Is there a way i can listen to application events and then process? Appreciate your help
You can write your own PropertiesFactoryBean
and override createProperties
to decrypt encrypted values:
public class DecryptingPropertiesFactoryBean extends PropertiesFactoryBean {
@Override
protected Properties createProperties() throws IOException {
final Properties encryptedProperties = super.createProperties();
final Properties decryptedProperties = decrypt(encryptedProperties);
return decryptedProperties;
}
}
and a PropertySourcesPlaceholderConfigurer
bean using these properties:
@Configuration
public class PropertiesConfiguration {
@Bean
public static DecryptingPropertiesFactoryBean propertyFactory() {
final DecryptingPropertiesFactoryBean factory = new DecryptingPropertiesFactoryBean();
final Resource[] propertyLocations = new Resource[] {
new FileSystemResource(new File("path/to/file.properties"))
};
factory.setLocations(propertyLocations);
return factory;
}
@Bean
public static Properties properties() throws Exception {
return propertyFactory().getObject();
}
@Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
final PropertySourcesPlaceholderConfigurer bean = new PropertySourcesPlaceholderConfigurer();
bean.setIgnoreResourceNotFound(true);
bean.setIgnoreUnresolvablePlaceholders(false);
bean.setProperties(properties());
return bean;
}
}