refactor(security/crypto): 优化字段加解密相关代码

This commit is contained in:
2025-06-09 19:28:45 +08:00
parent eb7dfd4ed7
commit a4823dcb0b
2 changed files with 144 additions and 76 deletions

View File

@@ -16,6 +16,7 @@
package top.continew.starter.security.crypto.core;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.ReflectUtil;
import org.apache.ibatis.executor.resultset.ResultSetHandler;
@@ -24,6 +25,8 @@ import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.type.SimpleTypeRegistry;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import top.continew.starter.security.crypto.annotation.FieldEncrypt;
import top.continew.starter.security.crypto.autoconfigure.CryptoProperties;
import top.continew.starter.security.crypto.encryptor.IEncryptor;
@@ -41,6 +44,7 @@ import java.util.List;
@Intercepts({@Signature(type = ResultSetHandler.class, method = "handleResultSets", args = {Statement.class})})
public class MyBatisDecryptInterceptor extends AbstractMyBatisInterceptor implements Interceptor {
private static final Logger log = LoggerFactory.getLogger(MyBatisDecryptInterceptor.class);
private CryptoProperties properties;
public MyBatisDecryptInterceptor(CryptoProperties properties) {
@@ -53,31 +57,73 @@ public class MyBatisDecryptInterceptor extends AbstractMyBatisInterceptor implem
@Override
public Object intercept(Invocation invocation) throws Throwable {
Object obj = invocation.proceed();
if (null == obj || !(invocation.getTarget() instanceof ResultSetHandler)) {
if (obj == null) {
return null;
}
// 确保目标是 ResultSetHandler
if (!(invocation.getTarget() instanceof ResultSetHandler)) {
return obj;
}
List<?> resultList = (List<?>)obj;
// 处理查询结果
if (obj instanceof List<?> resultList) {
// 处理列表结果
this.decryptList(resultList);
} else {
// 处理单个对象结果
this.decryptObject(obj);
}
return obj;
}
/**
* 解密列表结果
*
* @param resultList 结果列表
*/
private void decryptList(List<?> resultList) {
if (CollUtil.isEmpty(resultList)) {
return;
}
for (Object result : resultList) {
// String、Integer、Long 等简单类型对象无需处理
if (SimpleTypeRegistry.isSimpleType(result.getClass())) {
decryptObject(result);
}
}
/**
* 解密单个对象结果
*
* @param result 结果对象
*/
private void decryptObject(Object result) {
if (result == null) {
return;
}
// String、Integer、Long 等简单类型对象无需处理
if (SimpleTypeRegistry.isSimpleType(result.getClass())) {
return;
}
// 获取所有字符串类型、需要解密的、有值字段
List<Field> fieldList = super.getEncryptFields(result);
if (fieldList.isEmpty()) {
return;
}
// 解密处理
for (Field field : fieldList) {
IEncryptor encryptor = super.getEncryptor(field.getAnnotation(FieldEncrypt.class));
Object fieldValue = ReflectUtil.getFieldValue(result, field);
if (fieldValue == null) {
continue;
}
// 获取所有字符串类型、需要解密的、有值字段
List<Field> fieldList = super.getEncryptFields(result);
// 解密处理
for (Field field : fieldList) {
IEncryptor encryptor = super.getEncryptor(field.getAnnotation(FieldEncrypt.class));
Object fieldValue = ReflectUtil.getFieldValue(result, field);
if (null == fieldValue) {
continue;
}
// 优先获取自定义对称加密算法密钥,获取不到时再获取全局配置
String password = ObjectUtil.defaultIfBlank(field.getAnnotation(FieldEncrypt.class)
.password(), properties.getPassword());
// 优先获取自定义对称加密算法密钥,获取不到时再获取全局配置
String password = ObjectUtil.defaultIfBlank(field.getAnnotation(FieldEncrypt.class).password(), properties
.getPassword());
try {
String ciphertext = encryptor.decrypt(fieldValue.toString(), password, properties.getPrivateKey());
ReflectUtil.setFieldValue(result, field, ciphertext);
} catch (Exception e) {
// 解密失败时保留原值,避免影响正常业务流程
log.warn("解密失败,请检查加密配置", e);
}
}
return resultList;
}
}

View File

@@ -28,8 +28,9 @@ import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import top.continew.starter.core.constant.StringConstants;
import top.continew.starter.core.exception.BaseException;
import top.continew.starter.security.crypto.annotation.FieldEncrypt;
import top.continew.starter.security.crypto.autoconfigure.CryptoProperties;
import top.continew.starter.security.crypto.encryptor.IEncryptor;
@@ -50,6 +51,7 @@ import java.util.regex.Pattern;
*/
public class MyBatisEncryptInterceptor extends AbstractMyBatisInterceptor implements InnerInterceptor {
private static final Logger log = LoggerFactory.getLogger(MyBatisEncryptInterceptor.class);
private static final Pattern PARAM_PAIRS_PATTERN = Pattern
.compile("#\\{ew\\.paramNameValuePairs\\.(" + Constants.WRAPPER_PARAM + "\\d+)\\}");
private final CryptoProperties properties;
@@ -65,7 +67,7 @@ public class MyBatisEncryptInterceptor extends AbstractMyBatisInterceptor implem
RowBounds rowBounds,
ResultHandler resultHandler,
BoundSql boundSql) {
if (null == parameterObject) {
if (parameterObject == null) {
return;
}
if (parameterObject instanceof Map parameterMap) {
@@ -75,7 +77,7 @@ public class MyBatisEncryptInterceptor extends AbstractMyBatisInterceptor implem
@Override
public void beforeUpdate(Executor executor, MappedStatement mappedStatement, Object parameterObject) {
if (null == parameterObject) {
if (parameterObject == null) {
return;
}
if (parameterObject instanceof Map parameterMap) {
@@ -87,6 +89,60 @@ public class MyBatisEncryptInterceptor extends AbstractMyBatisInterceptor implem
}
}
/**
* 加密查询参数(针对 Map 类型参数)
*
* @param parameterMap 参数
* @param mappedStatement 映射语句
*/
private void encryptQueryParameter(Map<String, Object> parameterMap, MappedStatement mappedStatement) {
Map<String, FieldEncrypt> encryptParameterMap = super.getEncryptParameters(mappedStatement);
for (Map.Entry<String, Object> parameterEntrySet : parameterMap.entrySet()) {
String parameterName = parameterEntrySet.getKey();
Object parameterValue = parameterEntrySet.getValue();
if (parameterValue == null || ClassUtil.isBasicType(parameterValue
.getClass()) || parameterValue instanceof AbstractWrapper) {
continue;
}
if (parameterValue instanceof String str) {
FieldEncrypt fieldEncrypt = encryptParameterMap.get(parameterName);
if (fieldEncrypt != null) {
parameterMap.put(parameterName, this.doEncrypt(str, fieldEncrypt));
}
} else {
// 实体参数
this.encryptEntity(super.getEncryptFields(parameterValue), parameterValue);
}
}
}
/**
* 处理实体加密
*
* @param fieldList 加密字段列表
* @param entity 实体
*/
private void encryptEntity(List<Field> fieldList, Object entity) {
for (Field field : fieldList) {
IEncryptor encryptor = super.getEncryptor(field.getAnnotation(FieldEncrypt.class));
Object fieldValue = ReflectUtil.getFieldValue(entity, field);
if (fieldValue == null) {
continue;
}
// 优先获取自定义对称加密算法密钥,获取不到时再获取全局配置
String password = ObjectUtil.defaultIfBlank(field.getAnnotation(FieldEncrypt.class).password(), properties
.getPassword());
String ciphertext = fieldValue.toString();
try {
ciphertext = encryptor.encrypt(fieldValue.toString(), password, properties.getPublicKey());
} catch (Exception e) {
// 加密失败时保留原值,避免影响正常业务流程
log.warn("加密失败,请检查加密配置", e);
}
ReflectUtil.setFieldValue(entity, field, ciphertext);
}
}
/**
* 加密 Map 类型数据(使用 @Param 注解的场景)
*
@@ -105,33 +161,6 @@ public class MyBatisEncryptInterceptor extends AbstractMyBatisInterceptor implem
}
}
/**
* 加密查询参数(针对 Map 类型参数)
*
* @param parameterMap 参数
* @param mappedStatement 映射语句
*/
private void encryptQueryParameter(Map<String, Object> parameterMap, MappedStatement mappedStatement) {
Map<String, FieldEncrypt> encryptParameterMap = super.getEncryptParameters(mappedStatement);
for (Map.Entry<String, Object> parameterEntrySet : parameterMap.entrySet()) {
String parameterName = parameterEntrySet.getKey();
Object parameterValue = parameterEntrySet.getValue();
if (null == parameterValue || ClassUtil.isBasicType(parameterValue
.getClass()) || parameterValue instanceof AbstractWrapper) {
continue;
}
if (parameterValue instanceof String str) {
FieldEncrypt fieldEncrypt = encryptParameterMap.get(parameterName);
if (fieldEncrypt != null) {
parameterMap.put(parameterName, this.doEncrypt(str, fieldEncrypt));
}
} else {
// 实体参数
this.encryptEntity(super.getEncryptFields(parameterValue), parameterValue);
}
}
}
/**
* 处理 UpdateWrapper 类型参数加密(针对 MP 的 UpdateWrapper、LambdaUpdateWrapper 等参数)
*
@@ -156,7 +185,7 @@ public class MyBatisEncryptInterceptor extends AbstractMyBatisInterceptor implem
propMap.put(elPart[0], elPart[1]);
});
// 获取加密字段
Class<?> entityClass = mappedStatement.getParameterMap().getType();
Class<?> entityClass = this.getEntityClass(updateWrapper, mappedStatement);
List<Field> encryptFieldList = super.getEncryptFields(entityClass);
for (Field field : encryptFieldList) {
FieldEncrypt fieldEncrypt = field.getAnnotation(FieldEncrypt.class);
@@ -175,32 +204,6 @@ public class MyBatisEncryptInterceptor extends AbstractMyBatisInterceptor implem
}
}
/**
* 处理实体加密
*
* @param fieldList 加密字段列表
* @param entity 实体
*/
private void encryptEntity(List<Field> fieldList, Object entity) {
for (Field field : fieldList) {
IEncryptor encryptor = super.getEncryptor(field.getAnnotation(FieldEncrypt.class));
Object fieldValue = ReflectUtil.getFieldValue(entity, field);
if (null == fieldValue) {
continue;
}
// 优先获取自定义对称加密算法密钥,获取不到时再获取全局配置
String password = ObjectUtil.defaultIfBlank(field.getAnnotation(FieldEncrypt.class).password(), properties
.getPassword());
String ciphertext;
try {
ciphertext = encryptor.encrypt(fieldValue.toString(), password, properties.getPublicKey());
} catch (Exception e) {
throw new BaseException(e);
}
ReflectUtil.setFieldValue(entity, field, ciphertext);
}
}
/**
* 处理加密
*
@@ -208,7 +211,7 @@ public class MyBatisEncryptInterceptor extends AbstractMyBatisInterceptor implem
* @param fieldEncrypt 字段加密注解
*/
private Object doEncrypt(Object parameterValue, FieldEncrypt fieldEncrypt) {
if (null == parameterValue) {
if (parameterValue == null) {
return null;
}
IEncryptor encryptor = super.getEncryptor(fieldEncrypt);
@@ -217,7 +220,26 @@ public class MyBatisEncryptInterceptor extends AbstractMyBatisInterceptor implem
try {
return encryptor.encrypt(parameterValue.toString(), password, properties.getPublicKey());
} catch (Exception e) {
throw new BaseException(e);
// 加密失败时保留原值,避免影响正常业务流程
log.warn("加密失败,请检查加密配置", e);
}
return parameterValue;
}
/**
* 获取实体类
*
* @param wrapper 查询或更新包装器
* @param mappedStatement 映射语句
* @return 实体类
*/
private Class<?> getEntityClass(AbstractWrapper wrapper, MappedStatement mappedStatement) {
// 尝试从 Wrapper 中获取实体类
Class<?> entityClass = wrapper.getEntityClass();
if (entityClass != null) {
return entityClass;
}
// 从映射语句中获取实体类
return mappedStatement.getParameterMap().getType();
}
}