
目录
1.开发环境与工具
2.自定义注解接口:
2.1敏感类的注解接口(SensitiveData)
2.2敏感字段的注解接口(SensitiveField)
2.3注解接口的用法
3.注解注入后需要写加解密的拦截器
3.1加密拦截器(EncryptInterceptor)
3.2解密拦截器(DecryptInterceptor)
4.拦截器用到的SM4相关类
4.1 SM4加解密组件(Sm4crypto)
4.2 SM4工具类(Sm4Utils)
4.3国产加密相关依赖包
5.大功告成!
1.开发环境与工具
java,idea,mybatis-plus,spring boot
2.自定义注解接口: 2.1敏感类的注解接口(SensitiveData)import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import java.lang.annotation.*;
@documented
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Order(Ordered.HIGHEST_PRECEDENCE)
public @interface SensitiveData {
}
2.2敏感字段的注解接口(SensitiveField)
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import java.lang.annotation.*;
@documented
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Order(Ordered.HIGHEST_PRECEDENCE)
public @interface SensitiveField {
}
2.3注解接口的用法
举个例子,用户表(SysUser)中的手机(phone)字段需要进行加密处理,则需要在SysUser上加@SensitiveData进行注解,另外在phone字段上加@SensitiveField进行注解。
@SensitiveData
@EqualsAndHashCode(callSuper = true)
@Data
@TableName("sys_user")
public class SysUser extends baseEntity {
@TableId(type = IdType.ASSIGN_ID)
private Long id;
@Excel(name = "账号", width = 20)
private String account;
private String password;
@Excel(name = "姓名", width = 20)
private String name;
@SensitiveField
@Excel(name = "手机", width = 30)
private String phone;
private String salt;
}
3.注解注入后需要写加解密的拦截器
以上注解写完之后,我们需要写拦截器。让数据在后台存入数据库时进行拦截,拦截后将需要加密的字段在存入数据库之前使用SM4加密后再进行保存。同样的,在我们将数据从数据库中读出来时也需要进行拦截,拦截后使用SM4解密后拿到原值。
3.1加密拦截器(EncryptInterceptor)import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.executor.parameter.ParameterHandler;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Component;
import java.lang.reflect.Field;
import java.sql.PreparedStatement;
import java.util.ArrayList;
import java.util.Map;
import java.util.Objects;
import java.util.Properties;
@Component
@Intercepts({
@Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class}),
@Signature(type=Executor.class,method="query",args={MappedStatement.class,Object.class, RowBounds.class, ResultHandler.class})
})
public class EncryptInterceptor implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
MappedStatement statement = (MappedStatement) invocation.getArgs()[0];
// 获取该sql语句的类型,例如update,insert
String methodName = invocation.getMethod().getName();
// 获取该sql语句放入的参数
Object parameter = invocation.getArgs()[1];
if (StringUtils.equalsIgnoreCase("query", methodName)) {
Object result = invocation.proceed(); //执行请求方法,并将所得结果保存到result中
if (result instanceof ArrayList) {
ArrayList resultList = (ArrayList) result;
for (int i = 0; i < resultList.size(); i++) {
SensitiveData sensitiveData = AnnotationUtils.findAnnotation(resultList.get(i).getClass(), SensitiveData.class);
if (Objects.nonNull(sensitiveData)) {
Field[] declaredFields = resultList.get(i).getClass().getDeclaredFields();
}
}
}
return result;
}
// 拦截 Executor 的 update 方法 生成sql前将 tenantId 设置到实体中
if (StringUtils.equalsIgnoreCase("update", methodName) ||
StringUtils.equalsIgnoreCase("insert", methodName)) {
Class type = statement.getResultMaps().get(0).getClass();
if(parameter instanceof Map){
parameter = ((Map, ?>) parameter).get("param1");
}
SensitiveData sensitiveData = AnnotationUtils.findAnnotation(parameter.getClass(), SensitiveData.class);
if (Objects.nonNull(sensitiveData)) {
// 对参数内含注解的字段进行加密
Field[] declaredFields = parameter.getClass().getDeclaredFields();
Sm4crypto.encrypt(declaredFields, parameter);
}
}
return invocation.proceed();
}
@Override
public Object plugin(Object o) {
return Plugin.wrap(o, this);
}
@Override
public void setProperties(Properties properties) {
}
}
3.2解密拦截器(DecryptInterceptor)
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.executor.resultset.ResultSetHandler;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.*;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Objects;
import java.util.Properties;
@Component
@Intercepts({
@Signature(type = ResultSetHandler.class, method = "handleResultSets", args = {Statement.class}),
})
public class DecryptInterceptor implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
//取出查询的结果
Object resultObject = invocation.proceed();
if (Objects.isNull(resultObject)) {
return null;
}
//基于selectList
if (resultObject instanceof ArrayList) {
ArrayList resultList = (ArrayList) resultObject;
if (!CollectionUtils.isEmpty(resultList) && needToDecrypt(resultList.get(0))) {
for (Object result : resultList) {
//逐一解密
Sm4crypto.decrypt(result);
}
}
//基于selectOne
} else {
if (needToDecrypt(resultObject)) {
Sm4crypto.decrypt(resultObject);
}
}
return resultObject;
}
private boolean needToDecrypt(Object object) {
Class> objectClass = object.getClass();
SensitiveData sensitiveData = AnnotationUtils.findAnnotation(objectClass, SensitiveData.class);
return Objects.nonNull(sensitiveData);
}
@Override
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
@Override
public void setProperties(Properties properties) {
}
}
4.拦截器用到的SM4相关类
4.1 SM4加解密组件(Sm4crypto)
import cn.stylefeng.guns.core.util.Sm4Utils;
import org.springframework.stereotype.Component;
import java.lang.reflect.Field;
import java.util.Objects;
@Component
public class Sm4crypto {
public static final String DEFAULT_KEY = "自己的加解密key";
public static T encrypt(Field[] declaredFields, T paramsObject) throws IllegalAccessException {
for (Field field : declaredFields) {
//取出所有被EncryptDecryptField注解的字段
SensitiveField sensitiveField = field.getAnnotation(SensitiveField.class);
if (!Objects.isNull(sensitiveField)) {
field.setAccessible(true);
Object object = field.get(paramsObject);
//暂时只实现String类型的加密
if (object instanceof String) {
String value = (String) object;
String encryptValue = null;
try {
//加密 这里使用SM4加密工具
encryptValue = Sm4Utils.encryptEcb(DEFAULT_KEY, value);
} catch (Exception e) {
e.printStackTrace();
}
field.set(paramsObject, encryptValue);
}
}
}
return paramsObject;
}
public static T decrypt(T result) throws IllegalAccessException {
//取出resultType的类
Class> resultClass = result.getClass();
Field[] declaredFields = resultClass.getDeclaredFields();
for (Field field : declaredFields) {
//取出所有被EncryptDecryptField注解的字段
SensitiveField sensitiveField = field.getAnnotation(SensitiveField.class);
if (!Objects.isNull(sensitiveField)) {
field.setAccessible(true);
Object object = field.get(result);
//只支持String的解密
if (object instanceof String) {
String value = (String) object;
String decryptValue = null;
try {
//对注解的字段进行逐一解密
decryptValue = Sm4Utils.decryptEcb(DEFAULT_KEY, value);
} catch (Exception e) {
e.printStackTrace();
}
field.set(result, decryptValue);
}
}
}
return result;
}
}
4.2 SM4工具类(Sm4Utils)
import java.security.Key;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.SecureRandom;
import java.security.Security;
import java.util.Arrays;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.spec.SecretKeySpec;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.pqc.math.linearalgebra.ByteUtils;
public class Sm4Utils {
static {
Security.addProvider(new BouncyCastleProvider());
}
private static final String ENCODING = "UTF-8";
public static final String ALGORITHM_NAME = "SM4";
// 加密算法/分组加密模式/分组填充方式
// PKCS5Padding-以8个字节为一组进行分组加密
// 定义分组加密模式使用:PKCS5Padding
public static final String ALGORITHM_NAME_ECB_PADDING = "SM4/ECB/PKCS5Padding";
// 128-32位16进制;256-64位16进制
public static final int DEFAULT_KEY_SIZE = 128;
private static Cipher generateEcbCipher(String algorithmName, int mode, byte[] key) throws Exception {
Cipher cipher = Cipher.getInstance(algorithmName, BouncyCastleProvider.PROVIDER_NAME);
Key sm4Key = new SecretKeySpec(key, ALGORITHM_NAME);
cipher.init(mode, sm4Key);
return cipher;
}
public static byte[] generateKey() throws Exception {
return generateKey(DEFAULT_KEY_SIZE);
}
public static byte[] generateKey(int keySize) throws Exception {
KeyGenerator kg = KeyGenerator.getInstance(ALGORITHM_NAME, BouncyCastleProvider.PROVIDER_NAME);
kg.init(keySize, new SecureRandom());
return kg.generateKey().getEncoded();
}
public static String encryptEcb(String hexKey, String paramStr) throws Exception {
String cipherText = "";
// 16进制字符串-->byte[]
byte[] keyData = ByteUtils.fromHexString(hexKey);
// String-->byte[]
byte[] srcData = paramStr.getBytes(ENCODING);
// 加密后的数组
byte[] cipherArray = encrypt_Ecb_Padding(keyData, srcData);
// byte[]-->hexString
cipherText = ByteUtils.toHexString(cipherArray);
return cipherText;
}
public static byte[] encrypt_Ecb_Padding(byte[] key, byte[] data) throws Exception {
Cipher cipher = generateEcbCipher(ALGORITHM_NAME_ECB_PADDING, Cipher.ENCRYPT_MODE, key);
return cipher.doFinal(data);
}
public static String decryptEcb(String hexKey, String cipherText) throws Exception {
// 用于接收解密后的字符串
String decryptStr = "";
// hexString-->byte[]
byte[] keyData = ByteUtils.fromHexString(hexKey);
// hexString-->byte[]
byte[] cipherData = ByteUtils.fromHexString(cipherText);
// 解密
byte[] srcData = decrypt_Ecb_Padding(keyData, cipherData);
// byte[]-->String
decryptStr = new String(srcData, ENCODING);
return decryptStr;
}
public static byte[] decrypt_Ecb_Padding(byte[] key, byte[] cipherText) throws Exception {
Cipher cipher = generateEcbCipher(ALGORITHM_NAME_ECB_PADDING, Cipher.DECRYPT_MODE, key);
return cipher.doFinal(cipherText);
}
public static boolean verifyEcb(String hexKey, String cipherText, String paramStr) throws Exception {
// 用于接收校验结果
boolean flag = false;
// hexString-->byte[]
byte[] keyData = ByteUtils.fromHexString(hexKey);
// 将16进制字符串转换成数组
byte[] cipherData = ByteUtils.fromHexString(cipherText);
// 解密
byte[] decryptData = decrypt_Ecb_Padding(keyData, cipherData);
// 将原字符串转换成byte[]
byte[] srcData = paramStr.getBytes(ENCODING);
// 判断2个数组是否一致
flag = Arrays.equals(decryptData, srcData);
return flag;
}
}
4.3国产加密相关依赖包
5.大功告成!org.bouncycastle bcprov-jdk15on1.57 org.bouncycastle bcprov-ext-jdk15on1.57
欢迎分享,转载请注明来源:内存溢出
微信扫一扫
支付宝扫一扫
评论列表(0条)