springboot實(shí)現(xiàn)配置文件關(guān)鍵信息加解密
前言
在項(xiàng)目配置文件中常常會(huì)配置如數(shù)據(jù)庫(kù)連接信息、redis連接信息,而連接密碼明文配置在配置文件中會(huì)很不安全,所以就會(huì)將密碼信息加密后放在配置文件中,在啟動(dòng)項(xiàng)目時(shí)自動(dòng)解密轉(zhuǎn)換成明文后進(jìn)行連接,防止密碼泄露。
方案
1、實(shí)現(xiàn) EnvironmentPostProcessor 接口
2、引入 jasypt-spring-boot-starter 依賴(lài)
實(shí)踐
1、第一種方案
EnvironmentPostProcessor
是 Spring Boot 提供的一個(gè)接口,用于在 Spring Boot 的 Environment
初始化完成后對(duì)其進(jìn)行進(jìn)一步的處理。它允許你在 Spring Boot 的配置加載階段動(dòng)態(tài)修改或增強(qiáng) Environment
的內(nèi)容,例如添加額外的配置源、修改屬性值等。 ### 作用和用途
EnvironmentPostProcessor
的主要作用是在 Spring Boot 的啟動(dòng)過(guò)程中,對(duì) Environment
對(duì)象進(jìn)行擴(kuò)展或修改。它通常用于以下場(chǎng)景:
- 動(dòng)態(tài)添加配置源:例如,從遠(yuǎn)程配置中心(如 Spring Cloud Config Server、Consul 等)加載配置。
- 修改或覆蓋配置屬性:例如,根據(jù)環(huán)境變量或命令行參數(shù)動(dòng)態(tài)調(diào)整某些配置值。
- 解析加密的配置屬性:例如,解密配置文件中加密的敏感信息。
- 添加自定義的配置解析邏輯:例如,支持自定義的配置文件格式或解析規(guī)則。
使用方法
實(shí)現(xiàn) EnvironmentPostProcessor
接口,并將自定義實(shí)現(xiàn)類(lèi)注冊(cè)到springboot中
import org.springframework.boot.SpringApplication; import org.springframework.boot.env.EnvironmentPostProcessor; import org.springframework.core.Ordered; import org.springframework.core.annotation.Order; import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.MapPropertySource; import java.util.HashMap; import java.util.Map; /** * 自定義EnvironmentPostProcessor,在spring啟動(dòng)前將遍歷配置文件中是否有加密的值,將加密的值按自定義解密工具進(jìn)行解密 */ @Order(Ordered.HIGHEST_PRECEDENCE) public class DecryptEnvironmentPostProcessor implements EnvironmentPostProcessor { @Override public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) { environment.getPropertySources().forEach(propertySource -> { if (propertySource instanceof MapPropertySource) { MapPropertySource mapSource = (MapPropertySource) propertySource; Map<String, Object> originalSource = mapSource.getSource(); // 創(chuàng)建新的可修改 Map 副本 Map<String, Object> decryptedSource = new HashMap<>(originalSource); // 遍歷并解密值 decryptedSource.replaceAll((key, value) -> { if (value instanceof String) { String strValue = (String) value; if (strValue.startsWith("ENC(") && strValue.endsWith(")")) { String encryptedContent = strValue.substring(4, strValue.length() - 1); return AESEncryptionUtils.decrypt(encryptedContent); } } return value; }); // 用解密后的 Map 替換原 PropertySource environment.getPropertySources().replace( mapSource.getName(), new MapPropertySource(mapSource.getName(), decryptedSource) ); } }); } }
注冊(cè) EnvironmentPostProcessor
實(shí)現(xiàn)類(lèi)
在 META-INF/spring.factories
文件中注冊(cè)你的 EnvironmentPostProcessor
。
org.springframework.boot.env.EnvironmentPostProcessor=\ com.example.DecryptEnvironmentPostProcessor
自定義加解密工具類(lèi)
import javax.crypto.Cipher; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; import java.nio.charset.StandardCharsets; import java.util.Base64; import java.security.SecureRandom; public class AESEncryptionUtils { private static final String ALGORITHM = "AES/CBC/PKCS5Padding"; // 使用 CBC 模式 + PKCS5 填充 private static final String SECRET_KEY = "oJ51lypwUNzBIFXO"; // 密鑰環(huán)境變量名稱(chēng) /** * 加密明文 * @param plaintext 待加密的明文 * @return 格式為 "Base64(IV):Base64(密文)" 的字符串 */ public static String encrypt(String plaintext) { try { // 生成隨機(jī) IV byte[] ivBytes = new byte[16]; SecureRandom random = new SecureRandom(); random.nextBytes(ivBytes); IvParameterSpec iv = new IvParameterSpec(ivBytes); // 初始化加密器 SecretKeySpec keySpec = new SecretKeySpec(SECRET_KEY.getBytes(StandardCharsets.UTF_8), "AES"); Cipher cipher = Cipher.getInstance(ALGORITHM); cipher.init(Cipher.ENCRYPT_MODE, keySpec, iv); // 執(zhí)行加密 byte[] encryptedBytes = cipher.doFinal(plaintext.getBytes(StandardCharsets.UTF_8)); // 組合 IV 和密文,用 Base64 編碼 String ivBase64 = Base64.getEncoder().encodeToString(ivBytes); String encryptedBase64 = Base64.getEncoder().encodeToString(encryptedBytes); return ivBase64 + ":" + encryptedBase64; } catch (Exception e) { throw new RuntimeException("加密失敗", e); } } /** * 解密密文 * @param encryptedText 格式為 "Base64(IV):Base64(密文)" 的字符串 * @return 解密后的明文 */ public static String decrypt(String encryptedText) { try { // 拆分 IV 和密文 String[] parts = encryptedText.split(":"); if (parts.length != 2) { throw new IllegalArgumentException("無(wú)效的加密格式"); } byte[] ivBytes = Base64.getDecoder().decode(parts[0]); byte[] encryptedBytes = Base64.getDecoder().decode(parts[1]); // 初始化解密器 IvParameterSpec iv = new IvParameterSpec(ivBytes); SecretKeySpec keySpec = new SecretKeySpec(SECRET_KEY.getBytes(StandardCharsets.UTF_8), "AES"); Cipher cipher = Cipher.getInstance(ALGORITHM); cipher.init(Cipher.DECRYPT_MODE, keySpec, iv); // 執(zhí)行解密 byte[] decryptedBytes = cipher.doFinal(encryptedBytes); return new String(decryptedBytes, StandardCharsets.UTF_8); } catch (Exception e) { throw new RuntimeException("解密失敗", e); } } }
2、第二種方案
引入依賴(lài)
在項(xiàng)目的 pom.xml
文件中添加以下依賴(lài):
<dependency> <groupId>com.github.ulisesbocchio</groupId> <artifactId>jasypt-spring-boot-starter</artifactId> <version>3.0.4</version> </dependency>
配置 jasypt 信息
jasypt: encryptor: password: encryption-key algorithm: PBEWithMD5AndDES iv-generator-classname: org.jasypt.iv.NoIvGenerator
密碼加密
從 Jasypt 官方網(wǎng)站 或 Maven 中央倉(cāng)庫(kù)下載 jasypt-1.9.3.jar
。
使用 Jasypt 的命令行工具對(duì)配置信息進(jìn)行加密。例如,加密數(shù)據(jù)庫(kù)密碼:
java -cp jasypt-1.9.3.jar org.jasypt.intf.cli.JasyptPBEStringEncryptionCLI input="123456" password=encryption-key algorithm=PBEWithMD5AndDES
上述命令會(huì)輸出一個(gè)加密后的字符串,例如:
在配置文件中配置加密后的字符串:
spring: datasource: password: ENC(pxaWXichlG6mranljAUiZQ==)
在應(yīng)用啟動(dòng)時(shí),jasypt
會(huì)使用密鑰解密 ENC()
中的內(nèi)容,并將其值注入到對(duì)應(yīng)的配置屬性中。
jasypt 加解密工具類(lèi)
import org.jasypt.encryption.pbe.StandardPBEStringEncryptor; /** * jasypt 解密工具類(lèi) */ public class EncryptionUtil { public static String encrypt(String input, String password) { StandardPBEStringEncryptor encryptor = new StandardPBEStringEncryptor(); encryptor.setPassword(password); return encryptor.encrypt(input); } public static String decrypt(String encryptedValue, String password) { StandardPBEStringEncryptor encryptor = new StandardPBEStringEncryptor(); encryptor.setPassword(password); return encryptor.decrypt(encryptedValue); } }
補(bǔ)充
jasypt
密鑰配置到配置文件中其實(shí)也不太安全,更安全的做法是將密鑰配置到環(huán)境變量中或設(shè)置在啟動(dòng)命令中
export JASYPT_ENCRYPTOR_PASSWORD=your-encryption-key
java -jar your-application.jar --jasypt.encryptor.password=your-encryption-key
到此這篇關(guān)于springboot實(shí)現(xiàn)配置文件關(guān)鍵信息加解密的文章就介紹到這了,更多相關(guān)springboot配置文件信息加解密內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
解決Springboot不能自動(dòng)提交數(shù)據(jù)庫(kù)連接問(wèn)題
在使用SSM框架開(kāi)發(fā)時(shí),若在同一Service內(nèi)部方法間互相調(diào)用,直接使用this關(guān)鍵字會(huì)導(dǎo)致事務(wù)管理失效,從而引發(fā)如數(shù)據(jù)庫(kù)連接不足等問(wèn)題,原因是通過(guò)this調(diào)用不會(huì)經(jīng)過(guò)Spring的代理,因此不會(huì)自動(dòng)進(jìn)行事務(wù)處理2024-09-09java?zip文件解壓后無(wú)法刪除原zip文件問(wèn)題
這篇文章主要介紹了java?zip文件解壓后無(wú)法刪除原zip文件問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-06-06詳解Java后端優(yōu)雅驗(yàn)證參數(shù)合法性
這篇文章主要介紹了詳解Java后端優(yōu)雅驗(yàn)證參數(shù)合法性,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-02-02java如何將int數(shù)組轉(zhuǎn)化為Integer數(shù)組
這篇文章主要介紹了java如何將int數(shù)組轉(zhuǎn)化為Integer數(shù)組,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-11-11@TransactionalEventListener的使用和實(shí)現(xiàn)原理分析
這篇文章主要介紹了@TransactionalEventListener的使用和實(shí)現(xiàn)原理分析,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-12-12