应用程序侦听器中的Spring Load应用程序属性

时间:2019-05-30 05:40:44

标签: java spring spring-boot spring-mvc encryption

我试图在Spring中侦听应用程序事件并解密已加密(具有加密前缀)的应用程序属性。这个想法是创建一个Spring组件,如果属性值被加密,则在加载环境时将自动解密spring属性。

这需要与具有application.env.properties文件的旧版Spring应用程序一起使用(不带Spring Boot),以及具有yaml文件中定义的属性的最新Spring Boot应用程序。源,并且不应依赖于Spring Boot并可以与任何Spring版本一起使用。

public class DecryptingPropertiesListener
    implements ApplicationListener<ContextRefreshedEvent>, Ordered {
public static final String PREFIX_KEY = "{decrypt}";

private String prefix;
private Encrypter encrypter = Encrypter.defaultInstance();

@Override
public void onApplicationEvent(ContextRefreshedEvent event ) {
    Environment environment = event.getApplicationContext().getEnvironment();
    prefix = environment.getProperty(PREFIX_KEY, "{encrypted}");

    final MutablePropertySources propertySources = ((ConfigurableEnvironment) environment).getPropertySources();

    Set<String> encryptedKeys = getKeysOfEncryptedPropertyValues(environment, propertySources);
    addDecryptedValues(environment, propertySources, encryptedKeys);
}

private Set<String> getKeysOfEncryptedPropertyValues(Environment environment, MutablePropertySources propertySources) {
    return streamFromIterator(propertySources.iterator())
            .filter(EnumerablePropertySource.class::isInstance)
            .map(EnumerablePropertySource.class::cast)
            .flatMap(source -> asList(source.getPropertyNames()).stream())
            .filter(this::isNotEncryptionConfigProperty)
            .filter(key -> isEncrypted(environment.getProperty(key)))
            .collect(toSet());
}

private boolean isNotEncryptionConfigProperty(String key) {
    return !PREFIX_KEY.equals(key);
}

private Stream<PropertySource<?>> streamFromIterator(Iterator<PropertySource<?>> iterator) {
    Iterable<PropertySource<?>> iterable = () -> iterator;
    return StreamSupport.stream(iterable.spliterator(), false);
}

private void addDecryptedValues(Environment environment, MutablePropertySources propertySources, Set<String> encryptedKeys) {
    Map<String, Object> decryptedProperties = encryptedKeys.stream()
            .collect(toMap(
                    key -> key,
                    key -> decryptPropertyValue(environment.getProperty(key))));
    propertySources.addFirst(new MapPropertySource("decryptedValues", decryptedProperties));
}

private String decryptPropertyValue(String encryptedPropertyValue) {
    try {
        return encrypter.decryptIfEncrypted(encryptedPropertyValue);
    }
    catch (EncryptionException e) {
        throw new RuntimeException("Unable to decrypt property value '" + encryptedPropertyValue + "'", e);
    }
}

private boolean isEncrypted(Object propertyValue) {
    return propertyValue != null && propertyValue instanceof String && ((String)propertyValue).startsWith(prefix);
}

@Override
public int getOrder() {
    return Ordered.LOWEST_PRECEDENCE;
}

但是问题是,我没有看到此行返回的应用程序属性,((ConfigurableEnvironment)环境).getPropertySources(); 我可以在这里看到系统属性,但不能看到应用程序。知道如何在这里加载应用程序属性并对其解密吗? 谢谢

编辑:添加样本属性文件。想法是将此通用jar添加为各种Web应用程序的Maven依赖项,包括旧版和新版Spring Boot。 以下属性文件的格式为myapp.env.properties,并且myapp.system.properties具有定义的env。但是spring boot应用程序使用.yaml文件。服务和安全密码必须具有前缀,因此需要解密。

base.url=http://localhost:8080/myapp
service.password={decrypt}123456789==
security.password={decrypt}abcdefgh==
help.email.address=support@gmail.com

2 个答案:

答案 0 :(得分:1)

您可以使用EncryptablePropertyPlaceholderConfigurer并提供StringEncryptor的实例,它将为您处理属性解密。如果需要,还可以扩展该类以加载其他属性。

示例:


@Configuration
public class PropertyConfiguration {

  @Bean(name="envPropertyConfigurer")
  public EncryptablePropertyPlaceholderConfigurer getConfigurer() {
      return new EncryptablePropertyPlaceholderConfigurer (encryptor());
  }

  private StringEncryptor encryptor() {
     StandardPBEStringEncryptor s_encryptor = new StandardPBEStringEncryptor();
     s_encryptor.setAlgorithm("PBEWithMD5AndDES");
     s_encryptor.setPassword("secretKey");
     return s_encryptor;
  }
}

您还可以像这样在spring xml配置中执行此操作

<bean id="envPropertyEncryptor" class="org.jasypt.encryption.pbe.StandardPBEStringEncryptor">
    <property name="algorithm" value="PBEWithMD5AndDES" />
    <property name="algorithm" value="secretKey" />
</bean>
<bean id="envPropertyConfigurer" class="org.jasypt.spring.properties.EncryptablePropertyPlaceholderConfigurer">
     <constructor-arg ref="envPropertyEncryptor" />
</bean>

您也可以从文件中读取密钥,而不是在此处直接设置密钥。您可以通过使用config设置StandardPBEStringEncryptor FileStringPBEConfig属性来做到这一点。

答案 1 :(得分:0)

我结束了实现自定义ApplicationContextInitializer的过程,并覆盖了initialize()以从环境中解密属性并以高优先级插入它们,以便它们覆盖加密的值。

    public class PropertyContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {


       @Override
       public void initialize( ConfigurableApplicationContext applicationContext ) {
          ConfigurableEnvironment environment = applicationContext.getEnvironment();
          for ( PropertySource<?> propertySource : environment.getPropertySources() ) {
             Map<String, Object> propertyOverrides = new LinkedHashMap<>();
// call to decrypt method
             decryptProperty( propertySource, propertyOverrides );
             if ( !propertyOverrides.isEmpty() ) {
                PropertySource<?> decodedProperties = new MapPropertySource( "decrypt " + propertySource.getName(),
                      propertyOverrides );
                environment.getPropertySources().addBefore( propertySource.getName(), decodedProperties );
             }
          }
       }