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; package top.continew.starter.security.crypto.core;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.ReflectUtil; import cn.hutool.core.util.ReflectUtil;
import org.apache.ibatis.executor.resultset.ResultSetHandler; 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.Invocation;
import org.apache.ibatis.plugin.Signature; import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.type.SimpleTypeRegistry; 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.annotation.FieldEncrypt;
import top.continew.starter.security.crypto.autoconfigure.CryptoProperties; import top.continew.starter.security.crypto.autoconfigure.CryptoProperties;
import top.continew.starter.security.crypto.encryptor.IEncryptor; 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})}) @Intercepts({@Signature(type = ResultSetHandler.class, method = "handleResultSets", args = {Statement.class})})
public class MyBatisDecryptInterceptor extends AbstractMyBatisInterceptor implements Interceptor { public class MyBatisDecryptInterceptor extends AbstractMyBatisInterceptor implements Interceptor {
private static final Logger log = LoggerFactory.getLogger(MyBatisDecryptInterceptor.class);
private CryptoProperties properties; private CryptoProperties properties;
public MyBatisDecryptInterceptor(CryptoProperties properties) { public MyBatisDecryptInterceptor(CryptoProperties properties) {
@@ -53,31 +57,73 @@ public class MyBatisDecryptInterceptor extends AbstractMyBatisInterceptor implem
@Override @Override
public Object intercept(Invocation invocation) throws Throwable { public Object intercept(Invocation invocation) throws Throwable {
Object obj = invocation.proceed(); Object obj = invocation.proceed();
if (null == obj || !(invocation.getTarget() instanceof ResultSetHandler)) { if (obj == null) {
return null;
}
// 确保目标是 ResultSetHandler
if (!(invocation.getTarget() instanceof ResultSetHandler)) {
return obj; 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) { for (Object result : resultList) {
// String、Integer、Long 等简单类型对象无需处理 decryptObject(result);
if (SimpleTypeRegistry.isSimpleType(result.getClass())) { }
}
/**
* 解密单个对象结果
*
* @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; continue;
} }
// 获取所有字符串类型、需要解密的、有值字段 // 优先获取自定义对称加密算法密钥,获取不到时再获取全局配置
List<Field> fieldList = super.getEncryptFields(result); String password = ObjectUtil.defaultIfBlank(field.getAnnotation(FieldEncrypt.class).password(), properties
// 解密处理 .getPassword());
for (Field field : fieldList) { try {
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 ciphertext = encryptor.decrypt(fieldValue.toString(), password, properties.getPrivateKey()); String ciphertext = encryptor.decrypt(fieldValue.toString(), password, properties.getPrivateKey());
ReflectUtil.setFieldValue(result, field, ciphertext); 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.mapping.MappedStatement;
import org.apache.ibatis.session.ResultHandler; import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds; 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.constant.StringConstants;
import top.continew.starter.core.exception.BaseException;
import top.continew.starter.security.crypto.annotation.FieldEncrypt; import top.continew.starter.security.crypto.annotation.FieldEncrypt;
import top.continew.starter.security.crypto.autoconfigure.CryptoProperties; import top.continew.starter.security.crypto.autoconfigure.CryptoProperties;
import top.continew.starter.security.crypto.encryptor.IEncryptor; import top.continew.starter.security.crypto.encryptor.IEncryptor;
@@ -50,6 +51,7 @@ import java.util.regex.Pattern;
*/ */
public class MyBatisEncryptInterceptor extends AbstractMyBatisInterceptor implements InnerInterceptor { public class MyBatisEncryptInterceptor extends AbstractMyBatisInterceptor implements InnerInterceptor {
private static final Logger log = LoggerFactory.getLogger(MyBatisEncryptInterceptor.class);
private static final Pattern PARAM_PAIRS_PATTERN = Pattern private static final Pattern PARAM_PAIRS_PATTERN = Pattern
.compile("#\\{ew\\.paramNameValuePairs\\.(" + Constants.WRAPPER_PARAM + "\\d+)\\}"); .compile("#\\{ew\\.paramNameValuePairs\\.(" + Constants.WRAPPER_PARAM + "\\d+)\\}");
private final CryptoProperties properties; private final CryptoProperties properties;
@@ -65,7 +67,7 @@ public class MyBatisEncryptInterceptor extends AbstractMyBatisInterceptor implem
RowBounds rowBounds, RowBounds rowBounds,
ResultHandler resultHandler, ResultHandler resultHandler,
BoundSql boundSql) { BoundSql boundSql) {
if (null == parameterObject) { if (parameterObject == null) {
return; return;
} }
if (parameterObject instanceof Map parameterMap) { if (parameterObject instanceof Map parameterMap) {
@@ -75,7 +77,7 @@ public class MyBatisEncryptInterceptor extends AbstractMyBatisInterceptor implem
@Override @Override
public void beforeUpdate(Executor executor, MappedStatement mappedStatement, Object parameterObject) { public void beforeUpdate(Executor executor, MappedStatement mappedStatement, Object parameterObject) {
if (null == parameterObject) { if (parameterObject == null) {
return; return;
} }
if (parameterObject instanceof Map parameterMap) { 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 注解的场景) * 加密 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 等参数) * 处理 UpdateWrapper 类型参数加密(针对 MP 的 UpdateWrapper、LambdaUpdateWrapper 等参数)
* *
@@ -156,7 +185,7 @@ public class MyBatisEncryptInterceptor extends AbstractMyBatisInterceptor implem
propMap.put(elPart[0], elPart[1]); propMap.put(elPart[0], elPart[1]);
}); });
// 获取加密字段 // 获取加密字段
Class<?> entityClass = mappedStatement.getParameterMap().getType(); Class<?> entityClass = this.getEntityClass(updateWrapper, mappedStatement);
List<Field> encryptFieldList = super.getEncryptFields(entityClass); List<Field> encryptFieldList = super.getEncryptFields(entityClass);
for (Field field : encryptFieldList) { for (Field field : encryptFieldList) {
FieldEncrypt fieldEncrypt = field.getAnnotation(FieldEncrypt.class); 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 字段加密注解 * @param fieldEncrypt 字段加密注解
*/ */
private Object doEncrypt(Object parameterValue, FieldEncrypt fieldEncrypt) { private Object doEncrypt(Object parameterValue, FieldEncrypt fieldEncrypt) {
if (null == parameterValue) { if (parameterValue == null) {
return null; return null;
} }
IEncryptor encryptor = super.getEncryptor(fieldEncrypt); IEncryptor encryptor = super.getEncryptor(fieldEncrypt);
@@ -217,7 +220,26 @@ public class MyBatisEncryptInterceptor extends AbstractMyBatisInterceptor implem
try { try {
return encryptor.encrypt(parameterValue.toString(), password, properties.getPublicKey()); return encryptor.encrypt(parameterValue.toString(), password, properties.getPublicKey());
} catch (Exception e) { } 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();
} }
} }