2015-08-13 55 views
7

我的任務是在我們的配置文件中混淆密碼。雖然我不認爲這是正確的方法,但管理人員不同意......流程Spring Boot外部化屬性值

因此,我正在開發的項目基於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

我不認爲你可以,如果你只是使用內置到Spring中的東西。但是,如果您手動加載此YAML,然後將其送入Spring,請在此處執行此操作。 –

+0

我想我可以寫我自己的PropertySource。我希望找到適用於所有財產來源的機制。 –

+2

您可能對[Spring Boot問題]感興趣(https://github.com/spring-projects/spring-boot/issues/1312) –

回答

9

如果終於得到這個工作。 (主要感謝stephane-deracogithub

該解決方案的關鍵是實現ApplicationContextInitializer<ConfigurableApplicationContext>的類。我叫它PropertyPasswordDecodingContextInitializer

主要問題是讓春天使用這個ApplicationContextInitializer。重要信息請參見reference。我選擇使用具有以下內容的META-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在此階段尚未完成初始化,因此自動裝配或任何其他與bean相關的機制都不起作用。


更新:包括@jny的建議

+0

我注意到你在https://github.com/spring-projects/spring-boot/search?utf8=%E2%9C%93&q=.base64中使用了apache版本的base64代替你的代碼,爲什麼? – shareef

+0

基本上我並不在乎要使用什麼實現。我們已經使用apache commons庫,所以我沒有注意到在spring引導中也有一個。 –

3

我使用了@Daniele Torino的回答,並做了一些小改動。

首先,感謝他的鏈接,就如何使春天的選項承認初始化程序,我選擇了做它在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啓發的。下面是我在春天啓動的項目主要行動,以加密我的用戶名和密碼,並在項目解密他們的tomcat的工作:

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(注:部署在Tomcat上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 
相關問題