SpringBoot配置文件中密碼屬性加密的實現(xiàn)
本文主要介紹了SpringBoot配置文件中的明文密碼如何加密保存,讀取以及對于自定義的加密算法加密的參數(shù)如何保存和讀取。
背景
為了安全的需要,一些重要的信息比如數(shù)據(jù)庫密碼不能明文保存在配置文件中,需要進行加密之后再保存。SpringBoot可以使用jasypt-spring-boot這個組件來為配置屬性提供加密的支持。
集成jasypt-spring-boot到項目中
根據(jù)官方README文檔,可以有三種方式集成jasypt-spring-boot到項目中。
對于SpringBoot項目,直接通過引入jasypt-spring-boot-starter,然后所有的application.properties, application-*.properties, yaml的配置文件中就可以包含加密的屬性。
<dependency> <groupId>com.github.ulisesbocchio</groupId> <artifactId>jasypt-spring-boot-starter</artifactId> <version>3.0.4</version> </dependency>
直接引入jasypt-spring-boot,這時候需要在啟動類上添加一個@EnableEncryptableProperties,然后在配置文件中可以包含有加密的字段屬性。
@SpringBootApplication @EnableEncryptableProperties public class Application { ... }
如果不想要整個Spring的配置文件都啟用加密的字段屬性,還可以自己指定對應加密的配置文件路徑。也需要引入jasypt-spring-boot,同時在啟動類上添加@EncryptablePropertySource注解,設置注解的value屬性為需要讀取的加密配置文件路徑。
@SpringBootApplication @EncryptablePropertySource({"classpath:encrypted.properties"}) public class Application { ... }
配置文件配置加密與讀取
現(xiàn)在知道了如何集成jasypt-spring-boot到項目中,下面就介紹一下如何加密明文的密碼,以及需要如何存儲在配置文件中。
首先,需要添加一個maven的插件,這個插件可以幫助我們加密我們需要的明文信息。
<plugin> <groupId>com.github.ulisesbocchio</groupId> <artifactId>jasypt-maven-plugin</artifactId> <version>3.0.4</version> </plugin>
之后,執(zhí)行如下的mvn命令,可以在控制臺得到對于的密文。然后將配置文件中明文替換為加密后的密文。
mvn jasypt:encrypt-value -Djasypt.encryptor.password="TKzhc3fz" -Djasypt.plugin.value="123456"
-Djasypt.encryptor.password參數(shù)指定用于加密密碼,我理解這個應該和秘鑰類似。
-Djasypt.plugin.value參數(shù)指定需要加密的明文參數(shù)。
執(zhí)行命令后,可以在控制臺看到加密后的密文,密文默認是用ENC()格式包圍住的,當然這個格式也可以自定義。
也可以通過這個mvn插件解密,執(zhí)行如下命令,可以在控制臺看到解密之后的明文。
mvn jasypt:decrypt-value -Djasypt.encryptor.password="TKzhc3fz" -Djasypt.plugin.value="ENC(Muhq57xdiHA5jJX9pX7zmNz57w4emX2D/XYIXOMEx0LYcTL7RYyadWe2J7GCi9KJ)"
在SpringBoot配置文件中,添加jasypt.encryptor.password屬性,這個值和第二步生成密文的值要一樣,不然解密會失敗。
jasypt: encryptor: password: TKzhc3fz # 設置加密的password信息 類似秘鑰? test: password: ENC(Muhq57xdiHA5jJX9pX7zmNz57w4emX2D/XYIXOMEx0LYcTL7RYyadWe2J7GCi9KJ) # 測試數(shù)據(jù)
這樣在主程序啟動的時候,通過@Value注解就可以自動解密配置文件中的密文信息了,這樣就完成了明文的加密以及后續(xù)的讀取。
工作原理簡析
參照官方文檔,大概的工作原理如下:
首先,在Spring容器啟動之后,會遍歷配置文件中的所有的配置,然后發(fā)現(xiàn)所有按照jasypt約定規(guī)則加密的屬性,此處就是使用ENC()包圍的參數(shù),這個ENC()前綴和后綴是可以自定義的,下面會講到。
這邊主要是有兩個接口,分別是EncryptablePropertyDetector、EncryptablePropertyResolver,這兩個接口根據(jù)名稱可以看出來一個是發(fā)現(xiàn)器,一個是分解器。
先來看EncryptablePropertyDetector這個接口,這個接口提供了兩個方法,isEncrypted和unwrapEncryptedValue,isEncrypted方法判斷是否是jasypt約定規(guī)則加密的屬性,unwrapEncryptedValue方法會返回去除掉前綴和后綴的真正加密的值,可以看下該接口默認的實現(xiàn)DefaultPropertyDetector:
/** * Default property detector that detects encrypted property values with the format "$prefix$encrypted_value$suffix" * Default values are "ENC(" and ")" respectively. * * @author Ulises Bocchio */ public class DefaultPropertyDetector implements EncryptablePropertyDetector { // 默認的前綴和后綴 private String prefix = "ENC("; private String suffix = ")"; public DefaultPropertyDetector() { } public DefaultPropertyDetector(String prefix, String suffix) { Assert.notNull(prefix, "Prefix can't be null"); Assert.notNull(suffix, "Suffix can't be null"); this.prefix = prefix; this.suffix = suffix; } // 判斷配置屬性是否是按照jasypt約定規(guī)則加密的屬性 @Override public boolean isEncrypted(String property) { if (property == null) { return false; } final String trimmedValue = property.trim(); return (trimmedValue.startsWith(prefix) && trimmedValue.endsWith(suffix)); } // 去掉默認的前綴和后綴,返回加密的值 @Override public String unwrapEncryptedValue(String property) { return property.substring( prefix.length(), (property.length() - suffix.length())); } }
EncryptablePropertyResolver這個接口中只提供了一個方法resolvePropertyValue,這個方法會遍歷配置文件屬性,判斷是否是加密屬性,然后進行解密返回明文。在默認實現(xiàn)DefaultPropertyResolver中,依賴EncryptablePropertyDetector以及StringEncryptor,真正解密的方法是寫在StringEncryptor,這邊具體如何解密就不詳細描述了,有興趣可以自行看下。DefaultPropertyResolver類:
/** * @author Ulises Bocchio */ public class DefaultPropertyResolver implements EncryptablePropertyResolver { private final Environment environment; // 加密和解密的實現(xiàn) private StringEncryptor encryptor; // jasypt默認發(fā)現(xiàn)器 private EncryptablePropertyDetector detector; public DefaultPropertyResolver(StringEncryptor encryptor, Environment environment) { this(encryptor, new DefaultPropertyDetector(), environment); } public DefaultPropertyResolver(StringEncryptor encryptor, EncryptablePropertyDetector detector, Environment environment) { this.environment = environment; Assert.notNull(encryptor, "String encryptor can't be null"); Assert.notNull(detector, "Encryptable Property detector can't be null"); this.encryptor = encryptor; this.detector = detector; } @Override public String resolvePropertyValue(String value) { // 該方法獲取加密的屬性,然后使用StringEncryptor解密并返回 return Optional.ofNullable(value) .map(environment::resolvePlaceholders) .filter(detector::isEncrypted) // 過濾加密屬性 .map(resolvedValue -> { try { // 去除前綴和后綴獲取真正加密的值 String unwrappedProperty = detector.unwrapEncryptedValue(resolvedValue.trim()); String resolvedProperty = environment.resolvePlaceholders(unwrappedProperty); // 解密獲得明文 return encryptor.decrypt(resolvedProperty); } catch (EncryptionOperationNotPossibleException e) { throw new DecryptionException("Unable to decrypt property: " + value + " resolved to: " + resolvedValue + ". Decryption of Properties failed, make sure encryption/decryption " + "passwords match", e); } }) .orElse(value); } }
使用自定義的加密算法
如果不想要使用jasypt工具中的加密算法,或者內部要求使用某種特定的加密算法,jasypt-spring-boot組件也提供了自定義加解密的實現(xiàn)方式。上面在工作原理簡析中提到了兩個接口EncryptablePropertyDetector、EncryptablePropertyResolver,我們可以通過自己實現(xiàn)這兩個接口的方式,并且指定對應的bean名稱為encryptablePropertyDetector和encryptablePropertyResolver來覆蓋框架提供的默認實現(xiàn),完成加密算法和前綴后綴的自定義。
這邊我就用base64加密算法舉例,實現(xiàn)自定義的加密算法:
自己實現(xiàn)EncryptablePropertyDetector、EncryptablePropertyResolver接口,并且交給Spring管理,設置bean名稱為encryptablePropertyDetector和encryptablePropertyResolver。
重寫接口對應的方法。
@Component("encryptablePropertyDetector") public class Base64EncryptablePropertyDetector implements EncryptablePropertyDetector { private static final String PREFIX = "password:"; @Override public boolean isEncrypted(String property) { if (property == null) { return false; } return property.startsWith(PREFIX); } @Override public String unwrapEncryptedValue(String property) { return property.substring(PREFIX.length()); } }
@Component("encryptablePropertyResolver") public class Base64EncryptablePropertyResolver implements EncryptablePropertyResolver { @Autowired private Base64EncryptablePropertyDetector encryptablePropertyDetector; @Override public String resolvePropertyValue(String value) { return Optional.ofNullable(value) .filter(encryptablePropertyDetector::isEncrypted) .map(resolveValue -> { final String unwrapEncryptedValue = encryptablePropertyDetector.unwrapEncryptedValue(resolveValue); return new String(Base64.getDecoder().decode(unwrapEncryptedValue), StandardCharsets.UTF_8); }) .orElse(value); } }
配置文件中的加密屬性使用自定義的前綴和后綴。這邊明文先使用base64加密,之后加上“password:”前綴:
jasypt: encryptor: password: TKzhc3fz # 設置加密的password信息 類似秘鑰? test: password: password:MTIzNDU2 # 測試數(shù)據(jù)
啟動和讀取。
結語
參考鏈接:https://github.com/ulisesbocchio/jasypt-spring-boot
代碼地址:https://github.com/yzh19961031/SpringCloudDemo
到此這篇關于SpringBoot配置文件中密碼屬性加密的實現(xiàn)的文章就介紹到這了,更多相關SpringBoot 文件密碼屬性加密內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
apollo更改配置刷新@ConfigurationProperties配置類
這篇文章主要為大家介紹了apollo更改配置刷新@ConfigurationProperties配置類示例解析,apollo更改配置刷新@ConfigurationProperties配置類2023-04-04Java中system.exit(0) 和 system.exit(1)區(qū)別
本文主要介紹了Java中system.exit(0) 和 system.exit(1)區(qū)別,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2023-05-05