SpringBoot實現(xiàn)敏感配置信息加密與解密
一、為什么要加密配置?
先說說SpringBoot的配置加載機制。我們知道,SpringBoot支持多種配置加載方式,優(yōu)先級從高到低大概是:
- 命令行參數(shù)
- 環(huán)境變量
- application-{profile}.yml
- application.yml
- @ConfigurationProperties注解
不管用哪種方式,敏感信息(比如數(shù)據(jù)庫密碼、Redis密碼、第三方API密鑰)如果明文存儲,都會有泄露風險:
開發(fā)環(huán)境:本地配置文件可能被傳到Git 測試環(huán)境:測試服務器可能被非授權(quán)訪問 生產(chǎn)環(huán)境:配置文件權(quán)限管理不當,被運維人員誤看
更重要的是,《網(wǎng)絡安全法》明確要求對敏感信息進行加密存儲。去年某銀行就因為數(shù)據(jù)庫密碼明文存儲被罰款200萬。
所以,給配置文件加密不是選擇題,是必答題。
二、方案一:Jasypt集成(推薦)
Jasypt是SpringBoot生態(tài)中相對成熟的配置加密工具,開箱即用,配置簡單。
1. 添加依賴
添加Jasypt依賴
<dependency>
<groupId>com.github.ulisesbocchio</groupId>
<artifactId>jasypt-spring-boot-starter</artifactId>
<version>3.0.5</version>
</dependency>
2. 配置加密密鑰
在application.yml中添加Jasypt配置:
jasypt:
encryptor:
password: ${ENCRYPT_KEY} # 密鑰從環(huán)境變量獲取
algorithm: PBEWithHMACSHA512ANDAES_256 # 推薦使用強加密算法
iv-generator-classname: org.jasypt.iv.RandomIvGenerator
劃重點:密鑰千萬不要寫在配置文件里!這里用環(huán)境變量${ENCRYPT_KEY},Docker部署時通過-e ENCRYPT_KEY=xxx傳入。
3. 加密敏感配置
用Jasypt提供的命令行工具加密明文密碼:
# 下載jasypt-cli包 wget https://github.com/jasypt/jasypt/releases/download/jasypt-1.9.3/jasypt-1.9.3-dist.zip unzip jasypt-1.9.3-dist.zip # 執(zhí)行加密命令(密鑰用你的實際密鑰替換) java -cp jasypt-1.9.3/lib/jasypt-1.9.3.jar org.jasypt.intf.cli.JasyptPBEStringEncryptionCLI input="你的明文密碼" password=你的密鑰(32字節(jié)) algorithm=PBEWithHMACSHA512ANDAES_256 ivGeneratorClassName=org.jasypt.iv.RandomIvGenerator
執(zhí)行后會輸出加密后的密文,類似這樣:
----ENVIRONMENT-----------------
Runtime: OpenJDK 64-Bit Server VM 11.0.18+10
----ARGUMENTS-------------------
input: 你的明文密碼
password: 你的密鑰
algorithm: PBEWithHMACSHA512ANDAES_256
----OUTPUT----------------------
加密后的密文
4. 在配置文件中使用密文
把加密后的密文用ENC()包裹,放到配置文件里:
spring:
datasource:
url: jdbc:mysql://prod-db:3306/order_db
username: root
password: ENC(加密后的密文) # 用ENC()包裹
5. 啟動應用
啟動時傳入密鑰:
java -jar app.jar -Djasypt.encryptor.password=你的密鑰 # 或者用環(huán)境變量 ENCRYPT_KEY=你的密鑰 java -jar app.jar
三、方案二:自定義加密解密(適合有特殊需求)
如果Jasypt滿足不了你的需求(比如要對接企業(yè)內(nèi)部的密鑰管理系統(tǒng)),可以自己實現(xiàn)加密解密邏輯。
比如,某些時候項目需要用國密算法SM4,Jasypt不支持,就需要自定義個解密處理器。
1. 實現(xiàn)思路
SpringBoot啟動時會加載配置文件,我們可以通過自定義PropertySource,在配置被讀取前進行解密。
2. 代碼實現(xiàn)
第一步:創(chuàng)建加密工具類
package com.example.config.encrypt;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.security.Security;
import java.util.Base64;
/**
* 國密SM4加密工具類
* 依賴bcprov-jdk15on
*/
public class SM4Utils {
static {
Security.addProvider(new BouncyCastleProvider());
}
private static final String ALGORITHM_NAME = "SM4";
private static final String ALGORITHM_NAME_ECB_PADDING = "SM4/ECB/PKCS7Padding";
private SecretKeySpec secretKeySpec;
private Cipher cipher;
public SM4Utils(String key) {
try {
secretKeySpec = new SecretKeySpec(key.getBytes(), ALGORITHM_NAME);
cipher = Cipher.getInstance(ALGORITHM_NAME_ECB_PADDING, "BC");
} catch (Exception e) {
throw new RuntimeException("初始化SM4加密工具失敗", e);
}
}
// 加密
public String encrypt(String plainText) {
try {
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec);
byte[] encrypted = cipher.doFinal(plainText.getBytes());
return Base64.getEncoder().encodeToString(encrypted);
} catch (Exception e) {
throw new RuntimeException("SM4加密失敗", e);
}
}
// 解密
public String decrypt(String cipherText) {
try {
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec);
byte[] decrypted = cipher.doFinal(Base64.getDecoder().decode(cipherText));
return new String(decrypted);
} catch (Exception e) {
throw new RuntimeException("SM4解密失敗", e);
}
}
}
第二步:創(chuàng)建自定義PropertySource
package com.example.config.encrypt;
import org.springframework.core.env.PropertySource;
/**
* 自定義加密配置源
* 解密以"sm4:"開頭的配置項
*/
public class Sm4EncryptedPropertySource extends PropertySource<Object> {
private final PropertySource<?> source;
private final SM4Utils sm4Utils;
public Sm4EncryptedPropertySource(String name, PropertySource<?> source, String key) {
super(name);
this.source = source;
this.sm4Utils = new SM4Utils(key);
}
@Override
public Object getProperty(String name) {
Object value = source.getProperty(name);
if (value instanceof String) {
String strValue = (String) value;
// 解密以"sm4:"開頭的配置
if (strValue.startsWith("sm4:")) {
String cipherText = strValue.substring(4);
return sm4Utils.decrypt(cipherText);
}
}
return value;
}
}
第三步:注冊PropertySource
package com.example.config.encrypt;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.env.EnvironmentPostProcessor;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.MutablePropertySources;
import org.springframework.core.env.PropertySource;
/**
* 環(huán)境后置處理器,注冊自定義PropertySource
*/
public class EncryptedEnvironmentPostProcessor implements EnvironmentPostProcessor {
@Override
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
MutablePropertySources propertySources = environment.getPropertySources();
// 從環(huán)境變量獲取SM4密鑰
String sm4Key = environment.getProperty("SM4_KEY");
if (sm4Key == null) {
throw new RuntimeException("環(huán)境變量SM4_KEY未配置");
}
// 遍歷所有PropertySource,包裝需要解密的配置
for (PropertySource<?> source : propertySources) {
if (source.getName().contains("application.yaml")) {
propertySources.replace(source.getName(),
new Sm4EncryptedPropertySource(source.getName(), source, sm4Key));
}
}
}
}
第四步:配置spring.factories
在src/main/resources/META-INF/spring.factories中注冊后置處理器:
org.springframework.boot.env.EnvironmentPostProcessor=\ com.example.config.encrypt.EncryptedEnvironmentPostProcessor
3. 使用方式
在配置文件中用sm4:前綴標識需要加密的配置:
spring:
datasource:
password: sm4:加密后的密文 # 用SM4加密
啟動時傳入密鑰:
SM4_KEY=你的密鑰
java -jar app.jar
適用場景
- 需要更靈活的自定義敏感數(shù)據(jù)加解密邏輯
- 需要對接企業(yè)內(nèi)部密鑰管理系統(tǒng)(如從數(shù)據(jù)庫/接口獲取密鑰)
- 需要更細粒度的解密控制(如按配置項類型解密)
四、從開發(fā)到生產(chǎn)的完整流程
1. 開發(fā)環(huán)境
- 用Jasypt加密,密鑰存在本地環(huán)境變量
- IDEA配置Run Configuration,自動注入密鑰
- Gitignore過濾所有包含密鑰的配置文件
2. 測試環(huán)境
- Jenkins Pipeline從憑據(jù)管理獲取密鑰
- 構(gòu)建時自動加密配置文件
- 部署時通過環(huán)境變量注入密鑰
3. 生產(chǎn)環(huán)境
- 密鑰可以存儲在阿里云KMS
- 應用啟動時通過IAM角色獲取密鑰
- 配置文件通過Ansible或其他方式統(tǒng)一分發(fā)
4. 驗證方法
部署后一定要驗證配置是否解密成功,可以寫個簡單的接口:
@RestController
public class ConfigTestController {
@Value("${spring.datasource.password}")
private String dbPassword;
@GetMapping("/test/config")
public String testConfig() {
// 打印脫敏后的密碼(只顯示前3位和后3位)
if (dbPassword.length() > 6) {
return "密碼解密成功:" + dbPassword.substring(0, 3) + "******" + dbPassword.substring(dbPassword.length() - 3);
}
return "密碼解密成功";
}
}
五、總結(jié)
- 密鑰管理:密鑰必須與配置文件分離,優(yōu)先用環(huán)境變量或?qū)S妹荑€管理服務(如阿里云KMS)
- 算法選擇:優(yōu)先使用強加密算法(如AES-256、SM4),避免MD5、DES等弱算法
- 多環(huán)境隔離:開發(fā)/測試/生產(chǎn)環(huán)境使用不同密鑰,定期輪換
- 權(quán)限控制:配置文件權(quán)限設為600,僅應用進程可讀寫
- 審計日志:記錄配置解密操作,方便追溯
最后提醒一句:配置加密不是銀彈,還需要配合代碼審計、服務器安全加固等措施,才能真正保障系統(tǒng)安全。
到此這篇關(guān)于SpringBoot實現(xiàn)敏感配置信息加密與解密的文章就介紹到這了,更多相關(guān)SpringBoot敏感信息加解密內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
PowerJob的DesignateServer工作流程源碼解讀
這篇文章主要介紹了PowerJob的DesignateServer工作流程源碼解讀,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2024-01-01
Java中LinkedList數(shù)據(jù)結(jié)構(gòu)的詳細介紹
這篇文章主要介紹了Java中LinkedList,Linked List 是 java.util 包中 Collection 框架的一部分,文中提供了詳細的代碼說明,需要的朋友可以參考下2023-05-05
java socket實現(xiàn)聊天室 java實現(xiàn)多人聊天功能
這篇文章主要為大家詳細介紹了java socket實現(xiàn)聊天室,java實現(xiàn)多人聊天功能,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2018-07-07

