Creating a custom Jasypt PropertySource in Springboot

夙愿已清 提交于 2019-12-18 23:36:44

问题


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...


回答1:


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.




回答2:


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.



来源:https://stackoverflow.com/questions/24451110/creating-a-custom-jasypt-propertysource-in-springboot

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!