2015-08-13 3 views
7

У меня есть задача обфускации паролей в наших файлах конфигурации. Хотя я не думаю, что это правильный подход, менеджеры не согласны ...Запуск значений внешней загрузки Spring Spring

Итак, проект, над которым я работаю, основан на Spring Boot, и мы используем файлы конфигурации YAML. В настоящее время пароли в виде обычного текста:

spring: 
    datasource: 
     url: jdbc:sqlserver://DatabaseServer 
     driverClassName: com.microsoft.sqlserver.jdbc.SQLServerDriver 
     username: ele 
     password: NotTheRealPassword 

Идея заключается в том, чтобы иметь какой-то специальный синтаксис, который поддерживает обфусцированный или зашифрованный пароль:

spring: 
    datasource: 
     url: jdbc:sqlserver://DatabaseServer 
     driverClassName: com.microsoft.sqlserver.jdbc.SQLServerDriver 
     username: ele 
     password: password(Tm90VGhlUmVhbFBhc3N3b3Jk) 

Для того, чтобы это работало, я хочу, чтобы разобрать свойство значения с использованием регулярного выражения, и если он соответствует, замените значение деобфузированным/дешифрованным значением.

Но как перехватить значение свойства?

+0

Я не думаю, что вы можете, если вы просто используете что-то, встроенное в Весну. Однако, если вы загружаете этот YAML вручную, а затем загружаете его в Spring, делайте это там. –

+0

Я полагаю, что я мог бы написать свой собственный PropertySource. Я надеялся найти механизм, который применяется ко всем источникам собственности. –

+2

Возможно, вас заинтересует эта проблема с Spring Boot (https://github.com/spring-projects/spring-boot/issues/1312) –

ответ

9

Если, наконец, это получилось. (В основном благодаря 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 предложения.

+0

Я заметил, что вы использовали версию apache base64 вместо вашего кода в https://github.com/spring-projects/spring-boot/search?utf8=%E2%9C%93&q=.base64 почему? – shareef

+0

В принципе мне все равно, какую реализацию использовать. Мы уже пользуемся библиотеками apache-сообществ, поэтому я не заметил, что есть и весенняя загрузка. –

3

Я использовал ответ @Daniele Torino и сделал несколько незначительных изменений.

Во-первых, благодаря своей ссылки на варианты того, как сделать весной признать Initializer, я решил сделать это в Application: Второе

public static void main(String[] args) throws Exception { 
    SpringApplication application=new SpringApplication(Application.class); 
    application.addInitializers(new PropertyPasswordDecodingContextInitializer()); 
    application.run(args); 
} 

, IDEA сказал мне, что else if (source instanceof CompositePropertySource) { является избыточным, и это потому что CompositePropertySource наследует от EnumerablePropertySource.

В-третьих, я верю, что есть небольшая ошибка: она нарушает порядок разрешения собственности. Если у вас есть одно закодированное свойство в среде, а другое в файле application.properties, значение среды будет перезаписано значением application.properties. я изменил логику для вставки decodedProperties прямо перед закодированной:

 for (PropertySource<?> propertySource : environment.getPropertySources()) { 
       Map<String, Object> propertyOverrides = new LinkedHashMap<>(); 
       decodePasswords(propertySource, propertyOverrides); 
       if (!propertyOverrides.isEmpty()) { 
         environment.getPropertySources().addBefore(propertySource.getName(), new MapPropertySource("decoded"+propertySource.getName(), propertyOverrides)); 
       } 
     } 
+1

Я обновил ответ, чтобы включить ваши предложения. У вашего ответа, похоже, есть небольшая ошибка, в которой вы создаете два «MapPropertySource» - второй, принимающий 'decodedProperties' в качестве аргумента. Предполагается, что аргумент будет «Map», но является «PropertySource ». –

+0

Спасибо, я обновил код – jny

1

Вдохновленной @gogstad. Вот мое основное действие в рамках проекта весной загрузочном зашифровано имя пользователя и пароль и расшифрованы их в проекте для работы с котом:

1. В pom.xml файл

<dependency> 
     <groupId>com.github.ulisesbocchio</groupId> 
     <artifactId>jasypt-spring-boot</artifactId> 
     <version>1.12</version> 
    </dependency> 
    … 
    <build> 
     <resources> 
      <resource> 
       <directory>src/main/java</directory> 
       <includes> 
       <include>**/*.properties</include> 
       <include>**/*.xml</include> 
       </includes> 
       <targetPath>${project.build.directory}/classes</targetPath> 
      </resource> 
      <resource> 
       <directory>src/main/resources</directory> 
       <includes> 
        <include>**/*.properties</include> 
       </includes> 
       <targetPath>${project.build.directory}/classes</targetPath> 
     </resource> 
    </resources> 
    … 
    </build> 

2. в App.java (Примечание: для развертывания decryted springboot на коте, вы должны добавить @ServletComponentScan аннотации и расширяет SpringBootServletInitializer)

@SpringBootApplication 
    @ServletComponentScan 
    @EnableEncryptableProperties 
    @PropertySource(name="EncryptedProperties", value = "classpath:config/encrypted.properties") 
    public class App extends SpringBootServletInitializer { 
    public static void main(String[] args) throws Exception { 
     SpringApplication.run(App.class, args); 
     } 

    } 

3. Зашифрованные имя пользователя и пароль и заполнить application.properties файл с результатом:

java -cp ~/.m2/repository/org/jasypt/jasypt/1.9.2/jasypt-1.9.2.jar org.jasypt.intf.cli.JasyptPBEStringEncryptionCLI input="mypassword" password=mykey algorithm=PBEWithMD5AndDES 

выход походит ниже демо:

java -cp ~/.m2/repository/org/jasypt/jasypt/1.9.2/jasypt-1.9.2.jar org.jasypt.intf.cli.JasyptPBEStringEncryptionCLI input="mypassword" password=mykey algorithm=PBEWithMD5AndDES 

    ----ENVIRONMENT----------------- 

    Runtime: Oracle Corporation Java HotSpot(TM) 64-Bit Server VM 25.45-b02 



    ----ARGUMENTS------------------- 

    algorithm: PBEWithMD5AndDES 
    input: mypassword 
    password: mykey 



    ----OUTPUT---------------------- 

    5XNwZF4qoCKTO8M8KUjRprQbivTkmI8H 

4. под каталог src/main/resources/config добавить два файла свойств:

a. application.properties 
     spring.datasource.driver-class-name=com.mysql.jdbc.Driver 
     spring.datasource.url=jdbc:mysql://xxx 
     spring.datasource.username=ENC(xxx) 
     spring.datasource.password=ENC(xxx) 
     mybatis.mapper-locations=classpath:*/mapper/*.xml 
     mybatis.type-aliases-package=com.xx.xxx.model 
     logging.level.com.xx.xxx: DEBUG 

    b. encrypted.properties 
     jasypt.encryptor.password=mykey 
Смежные вопросы