0、問題背景
用 Spring Boot 框架的小伙伴應該都知道,Spring Boot 有個主要的 applicaiton 組態檔,那就會涉及到敏感配置資訊,比如各種中間件的連接用戶名密碼資訊、以及各種第三方的 KEY、密鑰等,
這種敏感資訊如果直接放在組態檔中肯定是不安全的,甚至在很多行業及領域(比如:支付領域)都是不合規的,所以需要保護 Spring Boot 中的敏感配置資訊,
所以,你還在讓你的 Spring Boot 系統裸奔嗎?如果是,那不妨看看本文中堆疊長分享的 4 種方法,讓你的系統不再裸奔!
1、配置中心(支持自動解密)
我覺得還得看大家的架構情況,如果使用了外置的第三方配置中心(支持自動解密的那種),就可以把所有的配置資訊存盤在配置中心,比如 Spring Cloud 生態中的配置中心,那么可以使用自帶的加、解密機制保護敏感資訊:
spring:
datasource:
username: '{cipher}t1s293294187a31f35dea15e8bafaf7774532xxcc20d6d6dd0dfa5ae753d6836'
需要加密的內容以 {cipher} 開頭標識,并注意要使用單引號包起來,具體的細節可以參考《Spring Cloud 配置中心內容加密》這篇文章,Spring Boot 組態檔就只存盤一些無關緊要的配置,
大家用的哪款配置中心呢?支付配置加解密嗎?歡迎分享!
如果沒有用到配置中心呢?
比如說傳統的 Spring Boot 的 MVC 專案,所有的代碼、配置都幾乎在同一個專案中,Spring Boot 中的核心組態檔就是 application.yml(.properties)檔案,那要怎么保護敏感配置資訊呢?繼續往下看!
2、資料庫機制
可以把所有配置資訊存盤到資料庫,系統啟動的時候全部加載進記憶體,存盤的時候,敏感資訊以對稱加密演算法進行加密存盤,然后加載的時候自動解密到記憶體,
這是最傳統的配置管理方法,其實你也可以理解為一個原始的、簡易的配置中心,只是功能不那么強大而已,因為現在很多配置中心就是把配置放在資料庫中進行存盤,然后提供一系列的配置管理功能,
這里的資料庫可以是關系資料庫(MySQL、Oracle)、記憶體資料庫(Redis、Zookeeper)等,這是普遍用的比較多的中間件技術,
3、自定義加解密機制
這時候也要看使用的程度,如果只是簡單的資料庫連接池資訊,那么可以考慮使用現有系統中的對稱加密演算法,再結合連接池資料源類實作自定義加解密機制,比如我們可以模仿 Spring Cloud 加密機制:
先用系統已有的對稱加密演算法對資料庫連接資訊加密:
spring:
datasource:
username: '{cipher}t1s293294187a31f35dea15e8bafaf7774532xxcc20d6d6dd0dfa5ae753d6836'
排除 Spring Boot 系統自帶的資料源自動配置,然后自行組裝資料源 Spring Bean,
判斷獲取的配置值是否以 {cipher} 這個標識開頭,如果是,則用系統約定的對稱加密演算法進行解密,然后再設定資料源,比如:
// 示例代碼
@Bean
public DataSource dataSource(){
DataSource dataSource = new DruidDataSource();
// 解密
String username = this.getUsername();
if (username.startWith('{cipher}')){
username = Encrypt.decrypt(username, this.getKey()))
}
dataSource.setUsername(username);
...
return dataSource;
}
Spring Boot 基礎就不介紹了,推薦下這個實戰教程,教程和示例原始碼都已經傳了:https://github.com/javastacks/spring-boot-best-practice
這種使用簡單,不用額外引入任何第三方包,如果大家也是使用的自定義資料源,或者這種手動加解密機制可以滿足保護其他敏感配置的需求,那么這種方案供大家參考,
上面介紹的自定義的加解密機制可以滿足一般的需求,如果是 Spring Boot 自動配置的場景,比如資料源自動配置,Redis 自動配置,等等,這種在系統啟動的時候就會默認自動配置,我們人工解密干預不到,
像這種情況,我們就需要考慮介入框架層了,在 Spring Boot 框架讀取配置的時候進行攔截解密,或者使用第三方的框架,用的比較多是:Jasypt Spring Boot,
4、Jasypt Spring Boot
Jasypt Spring Boot 是一個專門為 Spring Boot 專案中的屬性提供加密支持的框架,支持的版本為 Spring Boot 1.x ~ 2.x,堆疊長寫文之時,現在已經有 1.8K+ 的 Star 數了,還是挺受歡迎的,
開源地址:
https://github.com/ulisesbocchio/jasypt-spring-boot


這個開源專案更新也挺及時的,最新更新的,已支持 Spring Boot 2.5.4!
這里堆疊長再免費分享你一份 Spring Boot 學習筆記,理論和實戰都非常齊全,助你快速搞定 Spring Boot,
4.1 Jasypt Spring Boot 實戰
Jasypt Spring Boot 有 3 種集成方法:
1、如果開啟了 Spring Boot 的自動配置(使用了 @SpringBootApplication 或者 @EnableAutoConfiguration 注解): 只需要添加 jasypt-spring-boot-starter 依賴即可,這種會在整個 Spring Environment 中啟用可加密屬性;
2、添加 jasypt-spring-boot 依賴,同時在 Spring 主要配置類上添加 @EnableEncryptableProperties 注解,這種會在整個 Spring Environment 中啟用可加密屬性;
3、添加 jasypt-spring-boot 依賴,使用 @EncrytablePropertySource 注解宣告各個可加密的引數上,這種只適用于獨立配置引數加解密;
一般的 Spring Boot 都會開啟自動配置,然后再排除個別的自動配置,所以很少會有全部禁用自動配置的情況,不然使用 Spring Boot 的意義不大,這里我們使用第 1 種集成方式進行演示,
4.1.1 引入依賴
核心依賴:
<dependency>
<groupId>com.github.ulisesbocchio</groupId>
<artifactId>jasypt-spring-boot-starter</artifactId>
<version>3.0.4</version>
</dependency>
Maven 插件(可選)
<build>
<plugins>
<plugin>
<groupId>com.github.ulisesbocchio</groupId>
<artifactId>jasypt-maven-plugin</artifactId>
<version>${jasypt-spring-boot.version}</version>
</plugin>
</plugins>
</build>
4.1.2 添加密鑰
jasypt:
encryptor:
password: G9w0BAQEFAASCBKYwggSiAgEAAoIBAQC
property:
prefix: "ENC@["
suffix: "]"
這個 jasypt.encryptor.password 引數是必須的,相當于 Salt(鹽),以保證密碼安全性,prefix 和 prefix 是自定義的密碼串標識,不配置默認為:ENC(...),
4.1.3 敏感資訊加密
/**
* 來源微信公眾號:Java技術堆疊
* 作者:堆疊長
*/
@Slf4j
@RunWith(SpringRunner.class)
@SpringBootTest
public class JasyptTest {
@Autowired
private StringEncryptor stringEncryptor;
/**
* 來源微信公眾號:Java技術堆疊
* 作者:堆疊長
*/
@Test
public void encrypt() {
String usernameEnc = stringEncryptor.encrypt("javastack");
String passwordEnc = stringEncryptor.encrypt("javastack.cn");
log.info("test username encrypt is {}", usernameEnc);
log.info("test password encrypt is {}", passwordEnc);
log.info("test username is {}", stringEncryptor.decrypt(usernameEnc));
log.info("test password is {}", stringEncryptor.decrypt(passwordEnc));
}
}
這里我注入了一個 StringEncryptor,其類結構圖如下:

如果當前沒有自定義 StringEncryptor,Jasypt Spring Boot 的自動配置會默認創建一個 StringEncryptor 實體,直接用就行了,其構造器默認值如下:
| Key | Required | Default Value |
|---|---|---|
| jasypt.encryptor.password | True | - |
| jasypt.encryptor.algorithm | False | PBEWITHHMACSHA512ANDAES_256 |
| jasypt.encryptor.key-obtention-iterations | False | 1000 |
| jasypt.encryptor.pool-size | False | 1 |
| jasypt.encryptor.provider-name | False | SunJCE |
| jasypt.encryptor.provider-class-name | False | null |
| jasypt.encryptor.salt-generator-classname | False | org.jasypt.salt.RandomSaltGenerator |
| jasypt.encryptor.iv-generator-classname | False | org.jasypt.iv.RandomIvGenerator |
| jasypt.encryptor.string-output-type | False | base64 |
| jasypt.encryptor.proxy-property-sources | False | false |
| jasypt.encryptor.skip-property-sources | False | empty list |
然后運行測驗用例來看下測驗結果:

加解密成功!!
另外,通過 DEBUG 除錯可以看到是一個 DefaultLazyEncryptor 實體:

當然也支持自定義的 Encryptor,有需要的可以自行定制,
如果不想用測驗這種方法生成密文,也可以使用 Maven 插件,這就是前面為什么要加 Maven 插件(可選)的原因,使用方式如下:
mvn jasypt:encrypt-value -Djasypt.encryptor.password="G9w0BAQEFAASCBKYwggSiAgEAAoIBAQC" -Djasypt.plugin.value="https://www.cnblogs.com/javastack/p/javastack"
4.1.4 敏感資訊解密
將上一步生成的密文填充到 application 組態檔中:
javastack:
username: ENC@[K4DsOasic/5Cvu2Y6Ca5dyaw2+eejgqRfhDWB0itMWRONrIN+wLy3xkGbSfYxQ1b]
password: ENC@[UeZWoPt3ZhSs2wPUAKTF21dgnhzimB+FNNiQjpJoPEhwYzI5WH3IWboZ5Wn+5Rgf]
注意 ENC@[] 這個占位符是上面進行自定義配置的,
然后再寫一個程式嘗試列印出來:
/** * 來源微信公眾號:Java技術堆疊 * 作者:堆疊長 */@Slf4j@SpringBootApplicationpublic class Application { @Value("${javastack.username}") private String username; @Value("${javastack.password}") private String password; /** * 來源微信公眾號:Java技術堆疊 * 作者:堆疊長 */ public static void main(String[] args) { SpringApplication.run(Application.class); } /** * 來源微信公眾號:Java技術堆疊 * 作者:堆疊長 */ @Bean public CommandLineRunner commandLineRunner() { return (args) -> { log.info("javastack.username = {}", username); log.info("javastack.password = {}", password); }; }}
堆疊長寫了一個 CommandLineRunner,在系統啟動之后將密文的原文列印出來,不需要做任何處理,直接注入、列印就行,看是不是明文,
系統啟動之后:

結果正常,自動解密成功,
本節教程所有實戰原始碼已上傳到這個倉庫:
https://github.com/javastacks/spring-boot-best-practice
大家可以一鍵 Star,持續更新~
4.2 密鑰安全性
我們把 Jasypt 密鑰(password)存放在 application 組態檔中,這樣敏感資訊還是在專案代碼中,也不是太安全,建議通過命令列引數的方式傳入,如在 IDEA 中這樣設定:

如果是生產環境,可以通過命令的方式傳入:
java -Djasypt.encryptor.password=password -jar xx.jar
甚至還可以配置在服務器環境變數中,因為 StringEncryptor 可以通過 系統引數、組態檔、命令列引數、環境變數 等等方式進行構造,
這樣 Spring Boot 中的配置資訊就徹底安全了!
Jasypt Spring Boot 功能遠不止如此,實際功能要更強大,這里堆疊長只是介紹了簡單的運用,更多的自定義的需求大家可以參考官方檔案,那里有更詳細的教程,
4.3 Jasypt Spring Boot 原理
Jasypt Spring Boot 它注冊了一個 Spring 后處理器,它修飾包含在 Spring Environment 中的所有 PropertySource 物件,并按照 Jasypt 的配置約定對屬性進行加解密,
來跟一波原始碼:




原始碼有點復雜,一路找到了DefaultPropertyResolver 這個解密器,然后它也是注入了 StringEncryptor 這個實體,獲取配置時,會進行解密后再回傳,
另外,這個 Resolver 也是支持自定義的,有興趣的可以深入研究下,
總結
好了,今天堆疊長介紹了 Spring Boot 保護敏感配置資訊的 4 種方法,總結一下:
- 配置中心(支持自動加解密)
- 自定義加解密機制
- 資料庫機制
- Jasypt Spring Boot(第三方加解密方案)
總之敏感資訊不要放在 Spring Boot 組態檔中,一定要放,就一定要加密,這 4 種方案各有各的應用場景,要結合公司現有的架構和系統規模作出權衡,另外之前寫的這篇《分布式系統中處理引數配置的 4 種方案》也供大家參考下,
本節教程所有實戰原始碼已上傳到這個倉庫:
https://github.com/javastacks/spring-boot-best-practice
歡迎 Star 關注學習,后續會持續更新 Spring Boot 實戰教程,
好了,今天的分享就到這里了,后面堆疊長會分享更多好玩的 Java 技術和最新的技術資訊,關注公眾號Java技術堆疊第一時間推送,我也將主流 Java 面試題和參考答案都整理好了,在公眾號后臺回復關鍵字 "面試" 進行刷題,
著作權宣告: 本文系公眾號 "Java技術堆疊" 原創,轉載、參考本文內容請注明出處,抄襲、洗稿一律投訴侵權,后果自負,并保留追究其法律責任的權利,
近期熱文推薦:
1.1,000+ 道 Java面試題及答案整理(2022最新版)
2.勁爆!Java 協程要來了,,,
3.Spring Boot 2.x 教程,太全了!
4.別再寫滿屏的爆爆爆炸類了,試試裝飾器模式,這才是優雅的方式!!
5.《Java開發手冊(嵩山版)》最新發布,速速下載!
覺得不錯,別忘了隨手點贊+轉發哦!
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/443446.html
標籤:Java
上一篇:面試官:談談 CPU Cache 作業原理,Cache 一致性?我懵了。。
下一篇:Map 集合
