前言
.项目中我们存储一些用户信息的使用后根据规定,不可以存储明文,尤其是密码,实现的办法有好多种,今天承接上一篇文章mybatis拦截器,利用拦截器实现使用注解的方式在数据插入前进行加密,查询是自动进行解密的功能,前面提到过mybatis-plus拦截器用起来更方便一点,但是有个问题就是,如果使用了mybatis-plus拦截器就没办法在换其他的框架了
声明
此功能是根据自己需求改造其他大佬的项目而来(以下链接是原项目),目前仅用于自己测试没有问题,未经实际环境验证,请大家斟酌使用! mybatis-plus其实也支持此功能,但是他是收费项
https://blog.csdn.net/weixin_43655425/article/details/121394246
与原项目不同的是增加了对非对称加密算法的支持, 目前仅在里面实现的SM2,SM3,SM4三中算法,理论上加上非对称加密算法后应该是支持绝大多数算法,可根据需求直接更改源代码或实现ICrypto接口自定义
下载连接
里面的encrypt是实现加密注解的源代码,encrypt_demo为加密注解的演示demo
https://cowtransfer.com/s/f683e7cf5e9143
配置
增加配置项,目前的配置是支持国密SM2,SM3,SM4,如果不配置,将使用默认配置进行加密,对于密钥对的生成,提供了GenerateKeyPair工具类生成密钥对
#加密注解配置,如果不配置将使用默认配置加密
privacy:
crypto:
#密钥,用于SM4对称加密,随机16位字符串即可
key: qwerdhdhfgcbshur
#私钥公钥,用于SM2费对称加密,可用测试类中的方法生成替换
privateKey: 308193020100301306072a8648ce3d020106082a811ccf5501822d047930770201010420699d3f230b7ab5fe4520b550fb021585b648950ba1ec988a0730fa95fa809f26a00a06082a811ccf5501822da1440342000487a23a1bde2da2d7004dbfc9101aa30042880593108e99d7b47c72c139abe8159174dcac41c998b818ff9cec508fb9a732dbbd1f957d8fc423c164d42bb3d04c
publicKey: 3059301306072a8648ce3d020106082a811ccf5501822d0342000487a23a1bde2da2d7004dbfc9101aa30042880593108e99d7b47c72c139abe8159174dcac41c998b818ff9cec508fb9a732dbbd1f957d8fc423c164d42bb3d04c
/**
* 生成SM2非对称加密算法的密钥对,可用于替换yml配置文件中的 privateKey 和 publicKey
*
* @author zzt
* @version 1.0.0
* @date 2022/8/5 16:15
*/
public class GenerateKeyPair {
public static void main(String[] args) {
KeyPair pair = SecureUtil.generateKeyPair("SM2");
byte[] privateKey = pair.getPrivate().getEncoded();
byte[] publicKey = pair.getPublic().getEncoded();
String privateKeyStr = HexUtil.encodeHexStr(privateKey);
String publicKeyStr = HexUtil.encodeHexStr(publicKey);
System.out.println("privateKey: " + privateKeyStr);
System.out.println("publicKey: " + publicKeyStr);
}
}
用法
直接将注解添加到需要加密的字段上即可,其中注解中有三个参数需要注意含义:
key:是对插加密的密钥16位字符串即可,该项如果自定义进行了配置,当解密时需要使用相同的密钥否则无法解密
algorithm: 选择加密算法类型
iCrypto:加密实现类,当默认算法不满足是可自定义算法加密类
/**
* 用户信息
*
* @author zzt
* @version 1.0.0
* @date 2022/8/4 11:02
*/
@TableName("encrypt_demo")
@Data
@Accessors(chain = true)
public class EncryptDemoEntity {
@TableId
private Long id;
/**
* 指定SM2,非对称加密算法,该算法需要配置公钥和私钥,可在配置文件中配置
*/
@FieldEncrypt(algorithm = Algorithm.SM2)
@TableField(value = "USER_NAME")
private String userName;
/**
* 使用默认的SM4加密算法,该算法可以被解密
*/
@FieldEncrypt
@TableField(value = "PHONE_NUM")
private String phoneNum;
/**
* 使用自定义SM3算法,该算法不可以被解密,查询时将返回密文
*/
@FieldEncrypt(algorithm = Algorithm.SM3)
@TableField(value = "PASS_WORD")
private String passWord;
}
测试方法及结果
@SpringBootTest
@RunWith(SpringRunner.class)
class EncryptDemoApplicationTests {
@Autowired
private EncryptDemoMapper encryptDemoMapper;
/**
* 一般情况下使用默认SM4算法和指定SM3算法,SM2算法加密
*/
@Test
public void encryptTest1() {
EncryptDemoEntity entity = new EncryptDemoEntity();
Long id = System.currentTimeMillis();
entity.setId(id)
.setPhoneNum("15555555555")
.setUserName("admin")
.setPassWord("111111");
encryptDemoMapper.insert(entity);
System.out.println("加密后的结果: " + entity);
//加密后的结果: EncryptDemoEntity(id=1659596682578, userName=040EA7241AF8C0FA5F8816B3F6DBFC6A02CFF32BE7EE8EEF741183E64267DACC6773478A97D8CB8B60FFA9D3F4B691F2B96F5EB607A7995336FCB3CF5AA635E6EE58085EE9FDA5F7EBA29F7968E8FF6D47D3A111712E69801489EDE0D3F42F507322F09A1154, phoneNum=90cd9092b5c643ef966346ce1e3ad224, passWord=c7f66beee198fb411c8623e53cbbc6eb1e0f078b5d68ed7f10d02ffb0af46d44)
entity = encryptDemoMapper.selectById(id);
System.out.println("解密后的结果: " + entity);
//password字段使用的是SM3算法加密,不可被解密,所以返回的是密文
//解密后的结果: EncryptDemoEntity(id=1659596682578, userName=admin, phoneNum=15555555555, passWord=c7f66beee198fb411c8623e53cbbc6eb1e0f078b5d68ed7f10d02ffb0af46d44)
}
/**
* 给SM4对称加密算法指定自定义密钥
*/
@Test
public void encryptTest2() {
EncryptDemoVo vo = new EncryptDemoVo();
Long id = System.currentTimeMillis();
vo.setId(id)
.setUserName("root")
.setPhoneNum("15777777777")
.setPassWord("222222");
//如果把vo的数据复制到entity,vo的加密配置将会被entity覆盖,需要注意
encryptDemoMapper.insertVo(vo);
System.out.println("加密后的结果: " + vo);
//加密后的结果: EncryptDemoVo(id=1659597730091, userName=043E4074164748F73C108368A3C9017FE5396B807743CA536603FD1990584FC1A02ADADF23FBCD508B62788712CD85CF145248086E6D7A873E002B07405E9D0D2618C097D237445A4F8641368EA75257A916C77007F61424D966D574EB199F37AB1A4C54DD, phoneNum=8a089016eaba292348b6c81aeb170a00, passWord=8b30ba1c30168d83a5ef274f7b27f9aea4cda203ca0fc8965c1c5316dfb20309)
vo = encryptDemoMapper.selectVoById(id);
System.out.println("使用自定义密钥解密后的结果: " + vo);
//使用自定义密钥解密后的结果: EncryptDemoVo(id=1659597730091, userName=root, phoneNum=15777777777, passWord=8b30ba1c30168d83a5ef274f7b27f9aea4cda203ca0fc8965c1c5316dfb20309)
EncryptDemoTwoVo encryptDemoTwoVo = encryptDemoMapper.selectEncryptTwoVo(id);
System.out.println("使用默认密钥解密后的结果: " + encryptDemoTwoVo);
//注意这里EncryptDemoTwoVo里面的phoneNum使用的是默认密钥,但是这条数据我们使用的是自定义密钥加密的,所以解密结果是无法成功的,会报 BadPaddingException: pad block corrupted的异常信息!
//使用默认密钥解密后的结果: EncryptDemoTwoVo(id=1659598162187, userName=root, phoneNum=8a089016eaba292348b6c81aeb170a00, passWord=8b30ba1c30168d83a5ef274f7b27f9aea4cda203ca0fc8965c1c5316dfb20309)
}
/**
* 生成SM2非对称加密算法的密钥对,可用于替换yml配置文件中的 privateKey 和 publicKey
*/
@Test
public void generateKeyPair() {
KeyPair pair = SecureUtil.generateKeyPair("SM2");
byte[] privateKey = pair.getPrivate().getEncoded();
byte[] publicKey = pair.getPublic().getEncoded();
String privateKeyStr = HexUtil.encodeHexStr(privateKey);
String publicKeyStr = HexUtil.encodeHexStr(publicKey);
System.out.println("privateKey: " + privateKeyStr);
System.out.println("publicKey: " + publicKeyStr);
}
}
如有发现什么不足或可以改进的地方,欢迎指正讨论