2015-08-13 11 views
7

Mam za zadanie zaciemnianie haseł w naszych plikach konfiguracyjnych. Chociaż nie sądzę, że jest to właściwe podejście, menedżerowie nie zgadzają się ...Wiosenne wartości rozruchu procesu rozruchu wartości

Projekt, nad którym pracuję, jest oparty na Spring Boot i używamy plików konfiguracyjnych YAML. Obecnie są hasła w postaci zwykłego tekstu:

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

Chodzi o to, aby mieć jakąś specjalną składnię, która wspiera się zaciemniony lub zaszyfrowane hasło:

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

Aby to zadziałało chcę analizować właściwość wartości za pomocą wyrażenia regularnego i jeśli jest zgodne, zamień wartość na wartość odszyfrowaną/odszyfrowaną.

Ale jak mogę przechwycić wartość właściwości?

+0

Nie sądzę, że możesz, jeśli po prostu używasz czegoś wbudowanego w Spring. Jeśli jednak ładujesz ręcznie YAML, a następnie zasilasz go wiosną, zrób to. –

+0

Przypuszczam, że mógłbym napisać moje własne źródło danych. Miałem nadzieję znaleźć mechanizm, który dotyczy wszystkich źródeł własności. –

+2

Możesz być zainteresowany [ten Spring Boot problem] (https://github.com/spring-projects/spring-boot/issues/1312) –

Odpowiedz

9

Jeśli w końcu to zadziałało. (Głównie dzięki stephane-deraco pod numerem telefonu)

Kluczem do rozwiązania jest klasa implementująca ApplicationContextInitializer<ConfigurableApplicationContext>. Nazwałem to PropertyPasswordDecodingContextInitializer.

Głównym problemem było uzyskanie wiosny, aby korzystać z tego ApplicationContextInitializer. Ważne informacje można znaleźć w reference. Wybrałem metodę używając META-INF/spring.factories o następującej treści:

org.springframework.context.ApplicationContextInitializer=ch.mycompany.myproject.PropertyPasswordDecodingContextInitializer 

PropertyPasswordDecodingContextInitializer używa PropertyPasswordDecoder oraz klasę wykonawczą, obecnie dla uproszczenia 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); 
     } 
    } 


} 

Pamiętaj, że ApplicationContext nie został na tym etapie zainicjalizowany, więc automowowanie lub inne mechanizmy związane z komponentami bean nie będą działać.


Aktualizacja: wliczony @jny „s sugestie.

+0

Zauważyłem, że używasz apache wersji base64 zamiast swojego kodu w https://github.com/spring-projects/spring-boot/search?utf8=%E2%9C%93&q=.base64 dlaczego tak jest? – shareef

+0

Zasadniczo nie obchodziło mnie, jakiej implementacji użyć. Korzystamy już z bibliotek apache commons, więc nie zauważyłem, że jest też jeden z nich na wiosnę. –

3

Użyłem odpowiedzi @Daniele Torino i wprowadziłem kilka drobnych zmian.

Po pierwsze, dzięki jego link do opcji na jak zrobić sprężynę rozpoznać initializer, wybrałem to zrobić w Application:

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

drugie, IDEA powiedział mi, że else if (source instanceof CompositePropertySource) { jest zbędny i jest ponieważ CompositePropertySource dziedziczy po EnumerablePropertySource.

Po trzecie, uważam, że istnieje drobny błąd: zakłóca to kolejność rozwiązywania własności. Jeśli masz jedną zakodowaną właściwość w środowisku, a inną w pliku application.properties, wartość środowiska zostanie zastąpiona wartością application.properties. Zmieniłem logikę do wstawienia na decodedProperties tuż przed zakodowane:

 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

Zaktualizowałem odpowiedź, aby uwzględnić Twoje sugestie. Twoja odpowiedź wydaje się mieć mały błąd, w którym tworzysz dwa 'MapPropertySource' - drugi z nich przyjmuje argument' decodedProperties'. Argument ma być "Mapą", ale jest 'PropertySource '. –

+0

Dzięki, zaktualizowałem kod – jny

1

Zainspirowany @gogstad. Tu jest mój główny działania w projekcie bagażnika sprężyna szyfrowane moją nazwę użytkownika i hasło i rozszyfrował je w projekcie pracować z tomcat:

1. W pliku 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. W App.java (Uwaga: aby wdrożyć decryted springboot na Tomcat, należy dodać @ServletComponentScan adnotacji i rozszerza 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. Szyfrowane swój login i hasło, a następnie wypełnić plik application.properties z wynikiem:

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 

wyjście jest jak poniżej Demo:

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. Zgodnie z katalog src/main/resources/config dodaj dwa pliki właściwości:

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 
Powiązane problemy