行业知识
使用SM4国密加密算法对Spring Boot项目数据库连接信息以及yaml文件配置属性进行加密配置(读取时自动解密)
一、前言
在业务系统开发过程中,我们必不可少的会使用数据库,在应用开发过程中,数据库连接信息往往都是以明文的方式配置到yaml配置文件中的,这样有密码泄露的风险,那么有没有什么方式可以避免呢?方案当然是有的,就是对数据库密码配置的时候进行加密,然后读取的时候再进行解密,这样就可以避免敏感信息泄露了。
二、SM4算法
目前市面上流行的加密算法有很多,本次我们采用国产加密算法SM4进行介绍。
SM4加密算法是一种分组对接加密算法,详细信息可以参考百度百科:
SM4:SM4百科
三、数据库配置信息加解密
1. 导入相关加解密依赖
org.bouncycastle bcprov-jdk15to18 1.76 cn.hutool hutool-all 5.8.25
2. 编写加解密工具类
import cn.hutool.crypto.SmUtil;import cn.hutool.crypto.symmetric.SM4;public class SM4Utils { /** * SM4是对称加密,需要设置一个加解密秘钥 * * System.out.println(Arrays.toString("@Jhx2024#$%^&*!+".getBytes(StandardCharsets.UTF_8))); * 特别注意字符串key的长度需要16位 */ private static final byte[] keys = new byte[]{64, 74, 104, 120, 50, 48, 50, 52, 35, 36, 37, 94, 38, 42, 33, 43}; /** * 创建一个SM4加解密对象 */ private static final SM4 sm4 = SmUtil.sm4(keys); /** * 设置一个标识符,标识@SM4@- 开头的字符串是经过SM4加密的需要解密 */ public static final String SM4_PREFIX = "@SM4@-"; /** * 对字符串进行加密 * * @param value * @return */ public static String encryptStr(String value) { // 对加密的字符串添加前缀,方便标识这是一个加密以后的字符串 return SM4_PREFIX + sm4.encryptBase64(value); } /** * 对字符串进行解密 * * @param encryptValue * @return */ public static String decryptStr(String encryptValue) { // 解密时,需要去除加密标识符 return encryptValue.startsWith(SM4_PREFIX) ? sm4.decryptStr(encryptValue.substring(SM4_PREFIX.length())) : encryptValue; }}
注意:字符串key的长度需要16位,否则会报错:
Exception in thread "main" cn.hutool.crypto.CryptoException: InvalidKeyException: SM4 requires a 128 bit keyat cn.hutool.crypto.symmetric.SymmetricCrypto.encrypt(SymmetricCrypto.java:277)at cn.hutool.crypto.symmetric.SymmetricEncryptor.encrypt(SymmetricEncryptor.java:139)at cn.hutool.crypto.symmetric.SymmetricEncryptor.encryptBase64(SymmetricEncryptor.java:159)at com.learn.util.SM4Utils.encryptStr(SM4Utils.java:34)at com.learn.SnowFlakeDemoApplication.main(SnowFlakeDemoApplication.java:17)Caused by: java.security.InvalidKeyException: SM4 requires a 128 bit keyat org.bouncycastle.jcajce.provider.symmetric.util.BaseBlockCipher.engineInit(Unknown Source)at javax.crypto.Cipher.init(Cipher.java:1246)at javax.crypto.Cipher.init(Cipher.java:1186)at cn.hutool.crypto.CipherWrapper.initMode(CipherWrapper.java:116)at cn.hutool.crypto.symmetric.SymmetricCrypto.initMode(SymmetricCrypto.java:415)at cn.hutool.crypto.symmetric.SymmetricCrypto.encrypt(SymmetricCrypto.java:274)... 4 more
3. SM4工具类测试
-
测试代码
String str = "hello, world";System.out.println("原始字符串: " + str);str = SM4Utils.encryptStr(str);System.out.println("经过SM4默认秘钥加密字符串: " + str);str = SM4Utils.decryptStr(str);System.out.println("经过SM4默认秘钥解密字符串: " + str); -
测试结果
4. 对数据库配置信息进行加解密
1. 修改数据库配置信息
对数据库配置信息进行加密,加密信息的生成需要使用我们自定义的SM4Utils工具类:
spring: datasource: url: jdbc:mysql://127.0.0.1:3306/uid username: '@SM4@-tWyNqklSTiV5W3gN4dTQ2g==' password: '@SM4@-tWyNqklSTiV5W3gN4dTQ2g=='
此时,启动项目,数据库信息加载时,肯定会报错:
2. 自定义数据源解密
针对加密的数据库配置信息,需要自定义解密,所以需要自定义一个DataSource对象:
import com.learn.util.SM4Utils;import com.zaxxer.hikari.HikariDataSource;public class MyHikariDataSource extends HikariDataSource { @Override public String getUsername() { // 对用户名进行解密 return SM4Utils.decryptStr(super.getUsername()); } @Override public String getPassword() { // 对密码进行解密 return SM4Utils.decryptStr(super.getPassword()); }}3.修改数据库配置
spring: datasource: url: jdbc:mysql://127.0.0.1:3306/uid username: '@SM4@-tWyNqklSTiV5W3gN4dTQ2g==' # 经过SM4Utils.encryptStr方法加密 password: '@SM4@-tWyNqklSTiV5W3gN4dTQ2g==' # 经过SM4Utils.encryptStr方法加密 type: com.learn.db.MyHikariDataSource # 指定自定义的DataSource类
注意:我这里是使用的spring boot默认HikariDataSource数据源,所以自定义DataSource继承HikariDataSource类,如果是其他数据源,比如druid数据源,继承Druid的DataSource类即可。
4. 启动测试
启动不报错,业务正常,改造完成。
四、扩展——支持对所有配置属性加解密
针对上述对数据库加解密思路可以发现,这套逻辑也适用于针对spring boot所有属性的读取。
1. 导入jasypt-spring-boot-starter依赖
Jasypt为Spring Boot应用提供property sources的加密支持。
com.github.ulisesbocchio jasypt-spring-boot-starter 3.0.5
2. 自定义加密属性探测器实现
package com.learn.pro;import com.learn.util.SM4Utils;import com.ulisesbocchio.jasyptspringboot.EncryptablePropertyDetector;public class MyEncryptablePropertyDetector implements EncryptablePropertyDetector { /** * 判断是否是需要解密的字符串 * * @param s * @return */ @Override public boolean isEncrypted(String s) { if (s != null) { return s.startsWith(SM4Utils.SM4_PREFIX); // 判断是否有SM4加密标识 } return false; } /** * 获取截取掉加密标识以后的字符串 * * @param s * @return */ @Override public String unwrapEncryptedValue(String s) { return s.substring(SM4Utils.SM4_PREFIX.length()); }}3. 自定义密码解析器
package com.learn.pro;import com.learn.util.SM4Utils;import com.ulisesbocchio.jasyptspringboot.EncryptablePropertyResolver;public class MyEncryptablePropertyResolver implements EncryptablePropertyResolver { private MyEncryptablePropertyDetector encryptablePropertyDetector; public MyEncryptablePropertyResolver(MyEncryptablePropertyDetector encryptablePropertyDetector) { this.encryptablePropertyDetector = encryptablePropertyDetector; } @Override public String resolvePropertyValue(String s) { /** * 判断是否需要解密,如果需要解密则返回解密以后的值 */ if (encryptablePropertyDetector.isEncrypted(s)) { return SM4Utils.decryptStr(s); } return s; }}4. 自定义装配类生效
package com.learn.pro;import com.ulisesbocchio.jasyptspringboot.EncryptablePropertyResolver;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;@Configurationpublic class MyJasyptConfig { @Bean public MyEncryptablePropertyDetector encryptablePropertyDetector() { return new MyEncryptablePropertyDetector(); } @Bean public EncryptablePropertyResolver encryptablePropertyResolver() { return new MyEncryptablePropertyResolver(encryptablePropertyDetector()); }}5. 测试
我们这里自定义一个属性,进行加密测试,加密信息的生成需要使用我们自定义的SM4Utils工具类:
sm4: test: "@SM4@-1LnppEtMiTgGtTJ4shhYMg==" # SM4Utils.encryptStr("hello, SM4!")测试代码:
import org.springframework.beans.factory.annotation.Value;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import javax.annotation.PostConstruct;@SpringBootApplicationpublic class DemoApplication { @Value("${sm4.test}") private String sm4Test; private static DemoApplication demoApplication; @PostConstruct public void init() { demoApplication = this; } public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); System.out.println("\n获取属性sm4.test = "+ demoApplication.sm4Test); }}测试结果:
到此,一个通用的spring boot配置文件加密,默认解密功能就完成啦。
上一篇:易语言获取时间源码深度解析
下一篇:服务器放柜子里怎么散热?
最新文章
- steamApex英雄怎么换服务器?
- PostgreSQL数据库——Docker版本的postgres安装 & Navicat连接方式+导入向导使用 & SpringBoot结合Jpa使用PostgreSQL初步
- 揭秘谷歌中国风云变幻背后的故事
- QQ空间人气飙升秘籍揭秘,源码助力,轻松刷粉必备秘籍!
- 揭秘短视频原生态水印去除秘籍,无水印视频制作轻松上手!
- 服务器没有网怎么传输文件?
- 微软服务器在哪里?
- 基础前端使用web3 进行区块链项目开发
- 四季发财树折纸秘籍,轻松上手,财运滚滚,好运连连!
- Spring事件发布监听器ApplicationListener原理- 观察者模式