mirror of
https://github.com/continew-org/continew-starter.git
synced 2025-09-09 20:57:23 +08:00
refactor(security/crypto): 优化字段加解密相关代码
This commit is contained in:
@@ -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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user