I'm using Spring Boot to create a simple web application which accesses a database. I'm taking advantage of the autoconfiguration functionality for the DataSource by setting up spring.datasource.*
properties in application.properties
. That all works brilliantly and was very quick - great work guys @ Spring!
My companys policy is that there should be no clear text passwords. Therefore I need to have the sping.datasource.password
encrypted. After a bit of digging around I decided to create a org.springframework.boot.env.PropertySourceLoader
implementation which creates a jasypt org.jasypt.spring31.properties.EncryptablePropertiesPropertySource
as follows:
public class EncryptedPropertySourceLoader implements PropertySourceLoader
{
private final StandardPBEStringEncryptor encryptor = new StandardPBEStringEncryptor();
public EncryptedPropertySourceLoader()
{
//TODO: this could be taken from an environment variable
this.encryptor.setPassword("password");
}
@Override
public String[] getFileExtensions()
{
return new String[]{"properties"};
}
@Override
public PropertySource<?> load(final String name, final Resource resource, final String profile) throws IOException
{
if (profile == null)
{
final Properties props = PropertiesLoaderUtils.loadProperties(resource);
if (!props.isEmpty())
{
return new EncryptablePropertiesPropertySource(name, props, this.encryptor);
}
}
return null;
}
}
I then packaged this in it's own jar with a META-INF/spring.factories
file as follows:
org.springframework.boot.env.PropertySourceLoader=com.mycompany.spring.boot.env.EncryptedPropertySourceLoader
This works perfectly when run from maven using mvn spring-boot:run
. The problem occurs when I run it as a standalone war using java -jar my-app.war
. The application still loads but fails when I try to connect to the database as the password value is still encrypted. Adding logging reveals that the EncryptedPropertySourceLoader
is never loaded.
To me this sounds like a classpath issue. When run under maven the jar loading order is strict but once under the embebed tomcat there is nothing to say that my custom jar should be loaded before Spring Boot.
I've tried adding the following to my pom.xml to ensure the classpth is preserved but it doesn't seem to have had any effect.
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<configuration>
<failOnMissingWebXml>false</failOnMissingWebXml>
<archive>
<manifest>
<mainClass>${start-class}</mainClass>
<addClasspath>true</addClasspath>
</manifest>
</archive>
</configuration>
</plugin>
</plugins>
</pluginManagement>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
Does anyone have any ideas? Thanks in advance.
UPDATE:
A step forward: I've managed to fix this by having the EncryptedPropertySourceLoader
class implement org.springframework.core.PriorityOrdered
interface and returning HIGHEST_PRECEDENCE
from getOrder()
. This has now fixed the issue of the PropertySourceLoader not being used. However it's now throwing the following error when it tries to decrypt the properties:
org.jasypt.exceptions.EncryptionInitializationException: java.security.NoSuchAlgorithmException: PBEWithMD5AndDES SecretKeyFactory not available
at org.jasypt.encryption.pbe.StandardPBEByteEncryptor.initialize(StandardPBEByteEncryptor.java:716)
at org.jasypt.encryption.pbe.StandardPBEStringEncryptor.initialize(StandardPBEStringEncryptor.java:553)
at org.jasypt.encryption.pbe.StandardPBEStringEncryptor.decrypt(StandardPBEStringEncryptor.java:705)
at org.jasypt.properties.PropertyValueEncryptionUtils.decrypt(PropertyValueEncryptionUtils.java:72)
at org.jasypt.properties.EncryptableProperties.decode(EncryptableProperties.java:230)
at org.jasypt.properties.EncryptableProperties.get(EncryptableProperties.java:209)
at org.springframework.core.env.MapPropertySource.getProperty(MapPropertySource.java:36)
at org.springframework.boot.env.EnumerableCompositePropertySource.getProperty(EnumerableCompositePropertySource.java:49)
at org.springframework.boot.context.config.ConfigFileApplicationListener$ConfigurationPropertySources.getProperty(ConfigFileApplicationListener.java:490)
Again this doesn't happen when running from mvn spring-boot:run
but does happen when running from the executable war file. Both scenarios use the same JVM (jdk1.6.0_35). Results on Google/Stackoverflow suggest this is an issue with the java security policy but as it does work when run from maven I think I can discount that. Possibly a packaging issue...
The required steps to use it are: Create an instance (using new). Set a password (using setPassword(String) or setPasswordCharArray(char[])). Perform the desired encrypt(String) or decrypt(String) operations.
There are two issues here.
1) The EncryptedPropertySourceLoader needs to be loaded higher than then standard PropertiesPropertySourceLoader. This can be achieved by implementing the PriorityOrder interface as follows:
public class EncryptedPropertySourceLoader implements PropertySourceLoader, PriorityOrdered
{
private final StandardPBEStringEncryptor encryptor = new StandardPBEStringEncryptor();
public EncryptedPropertySourceLoader()
{
this.encryptor.setPassword("password"); //TODO: this could be taken from an environment variable
}
@Override
public String[] getFileExtensions()
{
return new String[]{"properties"};
}
@Override
public PropertySource<?> load(final String name, final Resource resource, final String profile) throws IOException
{
if (profile == null)
{
//load the properties
final Properties props = PropertiesLoaderUtils.loadProperties(resource);
if (!props.isEmpty())
{
//create the encryptable properties property source
return new EncryptablePropertiesPropertySource(name, props, this.encryptor);
}
}
return null;
}
@Override
public int getOrder()
{
return HIGHEST_PRECEDENCE;
}
}
The org.springframework.core.io.support.SpringFactoriesLoader
class which loads the org.springframework.boot.env.PropertySourceLoader
from the META-INF/spring.factories
orders the results using org.springframework.core.OrderComparator
. Meaning that this class should be returned first and will be given the responsibility of providinging the PropertySourceLoader implementation for *.proerpties files.
2) The second is a class loading issue with the executable JAR/WAR which seems to be caused by a bug in version 1.1.2.RELEASE of Spring Boot on Windows. Dropping to version 1.1.1.RELEASE or to version 1.1.3.RELEASE solves the various issues with classes and proerpties file not being loaded when run outside of maven.
You could give this a try: jasypt-spring-boot It basically wraps all PropertySource present in the Environment with an encryptable version. The 2 things you gotta do once you import the library (adding the dependency if you use maven) is to annotate your @Configuration class with @EnableEncryptableProperties, and to configure the encryption algorithm and password through properties.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With