springboot專案資料庫密碼如何加密
前言
在我們日常開發中,我們可能很隨意把資料庫密碼直接明文暴露在配置檔案中,在開發環境可以這麼做,但是在生產環境,是相當不建議這麼做,畢竟安全無小事,誰也不知道哪天密碼就莫名其妙洩露了。今天就來聊聊在springboot專案中如何對資料庫密碼進行加密
正文
方案一、使用druid資料庫連線池對資料庫密碼加密
1、pom.xml引入druid包
為了方便其他的操作,這邊直接引入druid的starter
- <dependency>
- <groupId>com.alibaba</groupId>
- <artifactId>druid-spring-boot-starter</artifactId>
- <version>${druid.version}</version>
- </dependency>
2、利用com.alibaba.druid.filter.config.ConfigTools生成公私鑰
ps: 生成的方式有兩種,一種利用命令列生成,一種直接寫個工具類生成。本文示例直接採用工具類生成
工具類程式碼如下
- /**
- * alibaba druid加解密規則:
- * 明文密碼+私鑰(privateKey)加密=加密密碼
- * 加密密碼+公鑰(publicKey)解密=明文密碼
- */
- public final class DruidEncryptorUtils {
- private static String privateKey;
- private static String publicKey;
- static {
- try {
- String[] keyPair = ConfigTools.genKeyPair(512);
- privateKey = keyPair[0];
- System.out.println(String.format("privateKey-->%s",privateKey));
- publicKey = keyPair[1];
- System.out.println(String.format("publicKey-->%s",publicKey));
- } catch (NoSuchAlgorithmException e) {
- e.printStackTrace();
- } catch (NoSuchProviderException e) {
- e.printStackTrace();
- }
- }
- /**
- * 明文加密
- * @param plaintext
- * @return
- */
- @SneakyThrows
- public static String encode(String plaintext){
- System.out.println("明文字串:" + plaintext);
- String ciphertext = ConfigTools.encrypt(privateKey,plaintext);
- System.out.println("加密後字串:" + ciphertext);
- return ciphertext;
- }
- /**
- * 解密
- * @param ciphertext
- * @return
- */
- @SneakyThrows
- public static String decode(String ciphertext){
- System.out.println("加密字串:" + ciphertext);
- String plaintext = ConfigTools.decrypt(publicKey,ciphertext);
- System.out.println("解密後的字串:" + plaintext);
- return plaintext;
- }
3、修改資料庫的配置檔案內容資訊
a 、 修改密碼
把密碼替換成用DruidEncryptorUtils這個工具類生成的密碼
- password: ${DATASOURCE_PWD:HB5FmUeAI1U81YJrT/T6awImFg1/Az5o8imy765WkVJouOubC2H80jqmZrr8L9zWKuzS/8aGzuQ4YySAkhywnA==}
b、 filter開啟config
- filter:
- config:
- enabled: true
c、配置connectionProperties屬性
- connection-properties: config.decrypt=true;config.decrypt.key=${spring.datasource.publickey}
ps: spring.datasource.publickey為工具類生成的公鑰
附錄: 完整資料庫配置
- spring:
- datasource:
- type: com.alibaba.druid.pool.DruidDataSource
- driverClassName: com.mysql.cj.jdbc.Driver
- url: ${DATASOURCE_URL:jdbc:mysql://localhost:3306/demo?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai}
- username: ${DATASOURCE_USERNAME:root}
- password: ${DATASOURCE_PWD:HB5FmUeAI1U81YJrT/T6awImFg1/Az5o8imy765WkVJouOubC2H80jqmZrr8L9zWKuzS/8aGzuQ4YySAkhywnA==}
- publickey: MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAIvP9xF4RCM4oFiu47NZY15iqNOAB9K2Ml9fiTLa05CWaXK7uFwBImR7xltZM1frl6ahWAXJB6a/FSjtJkTZUJECAwEAAQ==
- druid:
- # 初始連線數
- initialSize: 5
- # 最小連線池數量
- minIdle: 10
- # 最大連線池數量
- maxActive: 20
- # 配置獲取連線等待超時的時間
- maxWait: 60000
- # 配置間隔多久才進行一次檢測,檢測需要關閉的空閒連線,單位是毫秒
- timeBetweenEvictionRunsMillis: 60000
- # 配置一個連線在池中最小生存的時間,單位是毫秒
- minEvictableIdleTimeMillis: 300000
- # 配置一個連線在池中最大生存的時間,單位是毫秒
- maxEvictableIdleTimeMillis: 900000
- # 配置檢測連線是否有效
- validationQuery: SELECT 1 FROM DUAL
- testWhileIdle: true
- testOnBorrow: false
- testOnReturn: false
- webStatFilter:
- enabled: true
- statViewServlet:
- enabled: true
- # 設定白名單,不填則允許所有訪問
- allow:
- url-pattern: /druid/*
- # 控制檯管理使用者名稱和密碼
- login-username:
- login-password:
- filter:
- stat:
- enabled: true
- # 慢SQL記錄
- log-slow-sql: true
- slow-sql-millis: 1000
- merge-sql: true
- wall:
- config:
- multi-statement-allow: true
- config:
- enabled: true
- connection-properties: config.decrypt=true;config.decrypt.key=${spring.datasource.publickey}
方案二:使用jasypt對資料庫密碼加密
1、pom.xml引入jasypt包
- <dependency>
- <groupId>com.github.ulisesbocchio</groupId>
- <artifactId>jasypt-spring-boot-starter</artifactId>
- <version>${jasypt.verison}</version>
- </dependency>
2、利用jasypt提供的工具類對明文密碼進行加密
加密工具類如下
- public final class JasyptEncryptorUtils {
- private static final String salt = "lybgeek";
- private static BasicTextEncryptor basicTextEncryptor = new BasicTextEncryptor();
- static {
- basicTextEncryptor.setPassword(salt);
- }
- private JasyptEncryptorUtils(){}
- /**
- * 明文加密
- * @param plaintext
- * @return
- */
- public static String encode(String plaintext){
- System.out.println("明文字串:" + plaintext);
- String ciphertext = basicTextEncryptor.encrypt(plaintext);
- System.out.println("加密後字串:" + ciphertext);
- return ciphertext;
- }
- /**
- * 解密
- * @param ciphertext
- * @return
- */
- public static String decode(String ciphertext){
- System.out.println("加密字串:" + ciphertext);
- ciphertext = "ENC(" + ciphertext + ")";
- if (PropertyValueEncryptionUtils.isEncryptedValue(ciphertext)){
- String plaintext = PropertyValueEncryptionUtils.decrypt(ciphertext,basicTextEncryptor);
- System.out.println("解密後的字串:" + plaintext);
- return plaintext;
- }
- System.out.println("解密失敗");
- return "";
- }
- }
3、修改資料庫的配置檔案內容資訊
a、 用ENC包裹用JasyptEncryptorUtils 生成的加密串
- password: ${DATASOURCE_PWD:ENC(P8m43qmzqN4c07DCTPey4Q==)}
b、 配置金鑰和指定加解密演算法
- jasypt:
- encryptor:
- password: lybgeek
- algorithm: PBEWithMD5AndDES
- iv-generator-classname: org.jasypt.iv.NoIvGenerator
因為我工具類使用的是加解密的工具類是BasicTextEncryptor,其對應配置加解密就是PBEWithMD5AndDES和org.jasypt.iv.NoIvGenerator
ps: 在生產環境中,建議使用如下方式配置金鑰,避免金鑰洩露
- java -jar -Djasypt.encryptor.password=lybgeek
附錄: 完整資料庫配置
- spring:
- datasource:
- type: com.alibaba.druid.pool.DruidDataSource
- driverClassName: com.mysql.cj.jdbc.Driver
- url: ${DATASOURCE_URL:ENC(kT/gwazwzaFNEp7OCbsgCQN7PHRohaTKJNdGVgLsW2cH67zqBVEq7mN0BTIXAeF4/Fvv4l7myLFx0y6ap4umod7C2VWgyRU5UQtKmdwzQN3hxVxktIkrFPn9DM6+YahM0xP+ppO9HaWqA2ral0ejBCvmor3WScJNHCAhI9kHjYc=)}
- username: ${DATASOURCE_USERNAME:ENC(rEQLlqM5nphqnsuPj3MlJw==)}
- password: ${DATASOURCE_PWD:ENC(P8m43qmzqN4c07DCTPey4Q==)}
- druid:
- # 初始連線數
- initialSize: 5
- # 最小連線池數量
- minIdle: 10
- # 最大連線池數量
- maxActive: 20
- # 配置獲取連線等待超時的時間
- maxWait: 60000
- # 配置間隔多久才進行一次檢測,檢測需要關閉的空閒連線,單位是毫秒
- timeBetweenEvictionRunsMillis: 60000
- # 配置一個連線在池中最小生存的時間,單位是毫秒
- minEvictableIdleTimeMillis: 300000
- # 配置一個連線在池中最大生存的時間,單位是毫秒
- maxEvictableIdleTimeMillis: 900000
- # 配置檢測連線是否有效
- validationQuery: SELECT 1 FROM DUAL
- testWhileIdle: true
- testOnBorrow: false
- testOnReturn: false
- webStatFilter:
- enabled: true
- statViewServlet:
- enabled: true
- # 設定白名單,不填則允許所有訪問
- allow:
- url-pattern: /druid/*
- # 控制檯管理使用者名稱和密碼
- login-username:
- login-password:
- filter:
- stat:
- enabled: true
- # 慢SQL記錄
- log-slow-sql: true
- slow-sql-millis: 1000
- merge-sql: true
- wall:
- config:
- multi-statement-allow: true
- jasypt:
- encryptor:
- password: lybgeek
- algorithm: PBEWithMD5AndDES
- iv-generator-classname: org.jasypt.iv.NoIvGenerator
方案三:自定義實現
實現原理: 利用spring後置處理器修改DataSource
1、自定義加解密工具類
- /**
- * 利用hutool封裝的加解密工具,以AES對稱加密演算法為例
- */
- public final class EncryptorUtils {
- private static String secretKey;
- static {
- secretKey = Hex.encodeHexString(SecureUtil.generateKey(SymmetricAlgorithm.AES.getValue()).getEncoded());
- System.out.println("secretKey-->" + secretKey);
- System.out.println("--------------------------------------------------------------------------------------");
- }
- /**
- * 明文加密
- * @param plaintext
- * @return
- */
- @SneakyThrows
- public static String encode(String plaintext){
- System.out.println("明文字串:" + plaintext);
- byte[] key = Hex.decodeHex(secretKey.toCharArray());
- String ciphertext = SecureUtil.aes(key).encryptHex(plaintext);
- System.out.println("加密後字串:" + ciphertext);
- return ciphertext;
- }
- /**
- * 解密
- * @param ciphertext
- * @return
- */
- @SneakyThrows
- public static String decode(String ciphertext){
- System.out.println("加密字串:" + ciphertext);
- byte[] key = Hex.decodeHex(secretKey.toCharArray());
- String plaintext = SecureUtil.aes(key).decryptStr(ciphertext);
- System.out.println("解密後的字串:" + plaintext);
- return plaintext;
- }
- /**
- * 明文加密
- * @param plaintext
- * @return
- */
- @SneakyThrows
- public static String encode(String secretKey,String plaintext){
- System.out.println("明文字串:" + plaintext);
- byte[] key = Hex.decodeHex(secretKey.toCharArray());
- String ciphertext = SecureUtil.aes(key).encryptHex(plaintext);
- System.out.println("加密後字串:" + ciphertext);
- return ciphertext;
- }
- /**
- * 解密
- * @param ciphertext
- * @return
- */
- @SneakyThrows
- public static String decode(String secretKey,String ciphertext){
- System.out.println("加密字串:" + ciphertext);
- byte[] key = Hex.decodeHex(secretKey.toCharArray());
- String plaintext = SecureUtil.aes(key).decryptStr(ciphertext);
- System.out.println("解密後的字串:" + plaintext);
- return plaintext;
- }
- }
2、編寫後置處理器
- public class DruidDataSourceEncyptBeanPostProcessor implements BeanPostProcessor {
- private CustomEncryptProperties customEncryptProperties;
- private DataSourceProperties dataSourceProperties;
- public DruidDataSourceEncyptBeanPostProcessor(CustomEncryptProperties customEncryptProperties, DataSourceProperties dataSourceProperties) {
- this.customEncryptProperties = customEncryptProperties;
- this.dataSourceProperties = dataSourceProperties;
- }
- @Override
- public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
- if(bean instanceof DruidDataSource){
- if(customEncryptProperties.isEnabled()){
- DruidDataSource druidDataSource = (DruidDataSource)bean;
- System.out.println("--------------------------------------------------------------------------------------");
- String username = dataSourceProperties.getUsername();
- druidDataSource.setUsername(EncryptorUtils.decode(customEncryptProperties.getSecretKey(),username));
- System.out.println("--------------------------------------------------------------------------------------");
- String password = dataSourceProperties.getPassword();
- druidDataSource.setPassword(EncryptorUtils.decode(customEncryptProperties.getSecretKey(),password));
- System.out.println("--------------------------------------------------------------------------------------");
- String url = dataSourceProperties.getUrl();
- druidDataSource.setUrl(EncryptorUtils.decode(customEncryptProperties.getSecretKey(),url));
- System.out.println("--------------------------------------------------------------------------------------");
- }
- }
- return bean;
- }
- }
3、修改資料庫的配置檔案內容資訊
a 、 修改密碼
把密碼替換成用自定義加密工具類生成的加密密碼
- password: ${DATASOURCE_PWD:fb31cdd78a5fa2c43f530b849f1135e7}
b 、 指定金鑰和開啟加密功能
- custom:
- encrypt:
- enabled: true
- secret-key: 2f8ba810011e0973728afa3f28a0ecb6
ps: 同理secret-key最好也不要直接暴露在配置檔案中,可以用-Dcustom.encrypt.secret-key指定
附錄: 完整資料庫配置
- spring:
- datasource:
- type: com.alibaba.druid.pool.DruidDataSource
- driverClassName: com.mysql.cj.jdbc.Driver
- url: ${DATASOURCE_URL:dcb268cf3a2626381d2bc5c96f94fb3d7f99352e0e392362cb818a321b0ca61f3a8dad3aeb084242b745c61a1d3dc244ed1484bf745c858c44560dde10e60e90ac65f77ce2926676df7af6b35aefd2bb984ff9a868f1f9052ee9cae5572fa015b66a602f32df39fb1bbc36e04cc0f148e4d610a3e5d54f2eb7c57e4729c9d7b4}
- username: ${DATASOURCE_USERNAME:61db3bf3c6d3fe3ce87549c1af1e9061}
- password: ${DATASOURCE_PWD:fb31cdd78a5fa2c43f530b849f1135e7}
- druid:
- # 初始連線數
- initialSize: 5
- # 最小連線池數量
- minIdle: 10
- # 最大連線池數量
- maxActive: 20
- # 配置獲取連線等待超時的時間
- maxWait: 60000
- # 配置間隔多久才進行一次檢測,檢測需要關閉的空閒連線,單位是毫秒
- timeBetweenEvictionRunsMillis: 60000
- # 配置一個連線在池中最小生存的時間,單位是毫秒
- minEvictableIdleTimeMillis: 300000
- # 配置一個連線在池中最大生存的時間,單位是毫秒
- maxEvictableIdleTimeMillis: 900000
- # 配置檢測連線是否有效
- validationQuery: SELECT 1 FROM DUAL
- testWhileIdle: true
- testOnBorrow: false
- testOnReturn: false
- webStatFilter:
- enabled: true
- statViewServlet:
- enabled: true
- # 設定白名單,不填則允許所有訪問
- allow:
- url-pattern: /druid/*
- # 控制檯管理使用者名稱和密碼
- login-username:
- login-password:
- filter:
- stat:
- enabled: true
- # 慢SQL記錄
- log-slow-sql: true
- slow-sql-millis: 1000
- merge-sql: true
- wall:
- config:
- multi-statement-allow: true
- custom:
- encrypt:
- enabled: true
- secret-key: 2f8ba810011e0973728afa3f28a0ecb6
總結
上面三種方案,個人比較推薦用jasypt這種方案,因為它不僅可以對密碼加密,也可以對其他內容加密。而druid只能對資料庫密碼加密。至於自定義的方案,屬於練手,畢竟開源已經有的東西,就不要再自己造輪子了。
最後還有一個注意點就是jasypt如果是高於2版本,且以低於3.0.3,會導致配置中心,比如apollo或者nacos的動態重新整理配置失效(最新版的3.0.3官方說已經修復了這個問題)。
如果有使用配置中心的話,jasypt推薦使用3版本以下,或者使用3.0.3版本
demo連結
到此這篇關於springboot專案資料庫密碼如何加密的文章就介紹到這了,更多相關springboot資料庫密碼加密內容請搜尋我們以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援我們!