Geralmente, quando temos que referenciar um arquivo de propriedades, a maneira mais simples de se fazer é inserindo a seguinte tag no arquivo de contexto do Spring:
Dessa maneira podemos usar as propriedades contidas no arquivo application.properties para parametrizar atributos de configuração de algum bean, por exemplo. Podemos ver uma situação para o Data Source abaixo:
Para alterarmos as propriedades, entre outras coisas dela, o Spring fornece uma classe chamada PropertyPlaceHolderConfigurer. Ela permite alterarmos o comportamento padrão do property place holder em função de alguns métodos que estão como protected nela. Para o nosso caso, iremos criar uma classe que extenderá de PropertyPlaceholderConfigurer sobre escrevendo o método convertProperties. Porém, antes de criarmos ela, é importante termos uma outra classe que forneça serviços de criptografia e descriptografia em função de um password e de um algoritmo de cifragem, a fim de realizar a cifra com o combo password, valor da propriedade e do algoritmo, que para o caso desse tutorial será o PBEWithMD5AndDES, podendo ser alterado facilmente posteriormente. Segue um exemplo de implementação da classe:
Caso executássemos para criptografar o valor AtéQuando com o password ColocarSeuPasswordAqui, o valor retornado pelo método encrypt() seria Smzs1uObZlx21DsoCPaLoQ==. Beleza! Se no arquivo properties constava:
db.password = AtéQuando
Agora deve ficar como:
db.password = Smzs1uObZlx21DsoCPaLoQ==
Por fim, a classe com o override do método convertProperties:
Para finalizar, iremos substituir a entrada padrão do property-placeholder (conforme exemplo no começo da postagem) pelo bean:
Caso esteja configurado corretamente o log4j, a seguinte mensagem aparecerá no console:
INFO DecryptPropertyConfigurer - The following property was converted: db.password
Aqlbras! Ao som de Por Tudo que for, Lobão.
1 | < context:property-placeholder location = "classpath:application.properties" ></ context:property-placeholder > |
1 2 3 4 5 6 | < bean class = "org.springframework.jdbc.datasource.DriverManagerDataSource" id = "dataSource" > < property name = "driverClassName" value = "${db.driverClassName}" ></ property > < property name = "url" value = "${db.url}" ></ property > < property name = "username" value = "${db.username}" ></ property > < property name = "password" value = "${db.password}" ></ property > </ bean > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 | import java.io.IOException; import java.io.UnsupportedEncodingException; import java.security.GeneralSecurityException; import javax.crypto.Cipher; import javax.crypto.SecretKey; import javax.crypto.SecretKeyFactory; import javax.crypto.spec.PBEKeySpec; import javax.crypto.spec.PBEParameterSpec; import org.apache.log4j.LogManager; import org.apache.log4j.Logger; /** * It's necessary to set the static attribute char[] PASSWORD. It will be use to encrypt and decrypt * a text. This basically means initialing a javax.crypto.Cipher with algorithm * "PBEWithMD5AndDES" and getting a key from javax.crypto.SecretKeyFactory * with the same algorithm. * @author Willian Antunes * @version 1.0.0 * @see <a href="http://stackoverflow.com/questions/1132567/encrypt-password-in-configuration-files-java">Encrypt Password in Configuration Files</a> */ public class ProtectedConfigFile { private static final char [] PASSWORD = "ColocarSeuPasswordAqui" .toCharArray(); private static final byte [] SALT = { ( byte ) 0xde , ( byte ) 0x33 , ( byte ) 0x10 , ( byte ) 0x12 , ( byte ) 0xde , ( byte ) 0x33 , ( byte ) 0x10 , ( byte ) 0x12 , }; private static final Logger logger = LogManager.getLogger(ProtectedConfigFile. class ); public static String encrypt(String property) throws GeneralSecurityException, UnsupportedEncodingException { logger.debug( "Calling method encrypt(String property)..." ); SecretKeyFactory keyFactory = SecretKeyFactory.getInstance( "PBEWithMD5AndDES" ); SecretKey key = keyFactory.generateSecret( new PBEKeySpec(PASSWORD)); Cipher pbeCipher = Cipher.getInstance( "PBEWithMD5AndDES" ); pbeCipher.init(Cipher.ENCRYPT_MODE, key, new PBEParameterSpec(SALT, 20 )); return base64Encode(pbeCipher.doFinal(property.getBytes( "UTF-8" ))); } public static String decrypt(String property) throws GeneralSecurityException, IOException { logger.debug( "Calling method decrypt(String property)..." ); SecretKeyFactory keyFactory = SecretKeyFactory.getInstance( "PBEWithMD5AndDES" ); SecretKey key = keyFactory.generateSecret( new PBEKeySpec(PASSWORD)); Cipher pbeCipher = Cipher.getInstance( "PBEWithMD5AndDES" ); pbeCipher.init(Cipher.DECRYPT_MODE, key, new PBEParameterSpec(SALT, 20 )); return new String(pbeCipher.doFinal(base64Decode(property)), "UTF-8" ); } private static String base64Encode( byte [] bytes) { logger.debug( "Calling method base64Encode(byte[] bytes)..." ); return Encryption.base64Encode(bytes); } private static byte [] base64Decode(String property) throws IOException { logger.debug( "Calling method base64Decode(String property)..." ); return Encryption.base64Decode(property); } } |
db.password = AtéQuando
Agora deve ficar como:
db.password = Smzs1uObZlx21DsoCPaLoQ==
Por fim, a classe com o override do método convertProperties:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 | import java.io.IOException; import java.security.GeneralSecurityException; import java.util.Enumeration; import java.util.Properties; import org.apache.log4j.LogManager; import org.apache.log4j.Logger; import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer; import org.springframework.util.ObjectUtils; import br.com.willianantunes.util.security.ProtectedConfigFile; /** * Custom property place holder whose purpose is to convert a encrypted property to its normal value. * @author Willian Antunes * @version 1.0.0 * @see <a href="http://romiawasthy.blogspot.com.br/2012/02/encryptdecrpt-properties-in-spring.html">Encrypt/Decrypt Properties using PropertyPlaceholderConfigurer</a> */ public class DecryptPropertyConfigurer extends PropertyPlaceholderConfigurer { private final Logger logger = LogManager.getLogger(DecryptPropertyConfigurer. class ); @Override protected void convertProperties(Properties props) { logger.debug( "Calling method convertProperties(Properties propsy)..." ); Enumeration propertyNames = props.propertyNames(); while (propertyNames.hasMoreElements()) { String propertyName = (String) propertyNames.nextElement(); String propertyValue = props.getProperty(propertyName); String convertedValue; try { convertedValue = ProtectedConfigFile.decrypt(propertyValue); if (!ObjectUtils.nullSafeEquals(propertyValue, convertedValue)) { logger.info(String.format( "The following property was converted: %s" , propertyName)); props.setProperty(propertyName, convertedValue); } } catch (GeneralSecurityException e) { logger.debug(e); } catch (IOException e) { logger.debug(e); } } } } |
1 2 3 | < bean class = "br.com.willianantunes.springtest.init.DecryptPropertyConfigurer" > < property name = "location" value = "classpath:application.properties" ></ property > </ bean > |
INFO DecryptPropertyConfigurer - The following property was converted: db.password
Aqlbras! Ao som de Por Tudo que for, Lobão.
Comentários
Postar um comentário