mirror of
https://github.com/continew-org/continew-starter.git
synced 2025-09-08 16:57:09 +08:00
fix: 重构并修复更新场景加密失效的问题(仍需处理 MP 和单参查询加密问题)
This commit is contained in:
@@ -272,6 +272,11 @@ public class StringConstants {
|
||||
*/
|
||||
public static final String ROUND_BRACKET_END = ")";
|
||||
|
||||
/**
|
||||
* 等号(=)
|
||||
*/
|
||||
public static final String EQUALS = "=";
|
||||
|
||||
/**
|
||||
* 路径模式
|
||||
*/
|
||||
|
@@ -22,6 +22,7 @@ import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
|
||||
import com.baomidou.mybatisplus.extension.plugins.handler.DataPermissionHandler;
|
||||
import com.baomidou.mybatisplus.extension.plugins.inner.BlockAttackInnerInterceptor;
|
||||
import com.baomidou.mybatisplus.extension.plugins.inner.DataPermissionInterceptor;
|
||||
import com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor;
|
||||
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import org.mybatis.spring.annotation.MapperScan;
|
||||
@@ -43,6 +44,8 @@ import top.continew.starter.data.mp.datapermission.DataPermissionFilter;
|
||||
import top.continew.starter.data.mp.datapermission.DataPermissionHandlerImpl;
|
||||
import top.continew.starter.data.mp.handler.MybatisBaseEnumTypeHandler;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* MyBatis Plus 自动配置
|
||||
*
|
||||
@@ -76,6 +79,11 @@ public class MybatisPlusAutoConfiguration {
|
||||
@ConditionalOnMissingBean
|
||||
public MybatisPlusInterceptor mybatisPlusInterceptor(MyBatisPlusExtensionProperties properties) {
|
||||
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
|
||||
// 其他拦截器
|
||||
Map<String, InnerInterceptor> innerInterceptors = SpringUtil.getBeansOfType(InnerInterceptor.class);
|
||||
if (!innerInterceptors.isEmpty()) {
|
||||
innerInterceptors.values().forEach(interceptor::addInnerInterceptor);
|
||||
}
|
||||
// 数据权限插件
|
||||
MyBatisPlusExtensionProperties.DataPermissionProperties dataPermissionProperties = properties
|
||||
.getDataPermission();
|
||||
|
@@ -137,6 +137,11 @@
|
||||
<artifactId>mybatis-plus-core</artifactId>
|
||||
<version>${mybatis-plus.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.baomidou</groupId>
|
||||
<artifactId>mybatis-plus-extension</artifactId>
|
||||
<version>${mybatis-plus.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- MyBatis Flex(MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,简化开发、提高效率) -->
|
||||
<dependency>
|
||||
|
@@ -22,7 +22,7 @@
|
||||
<!-- MyBatis Plus(MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,简化开发、提高效率) -->
|
||||
<dependency>
|
||||
<groupId>com.baomidou</groupId>
|
||||
<artifactId>mybatis-plus-core</artifactId>
|
||||
<artifactId>mybatis-plus-extension</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
@@ -50,7 +50,7 @@ public class CryptoAutoConfiguration {
|
||||
* MyBatis 加密拦截器配置
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(MyBatisEncryptInterceptor.class)
|
||||
@ConditionalOnMissingBean
|
||||
public MyBatisEncryptInterceptor myBatisEncryptInterceptor() {
|
||||
return new MyBatisEncryptInterceptor(properties);
|
||||
}
|
||||
|
@@ -16,27 +16,14 @@
|
||||
|
||||
package top.continew.starter.security.crypto.core;
|
||||
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import cn.hutool.core.text.CharSequenceUtil;
|
||||
import cn.hutool.core.util.ReflectUtil;
|
||||
import cn.hutool.extra.spring.SpringUtil;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Constants;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.apache.ibatis.mapping.MappedStatement;
|
||||
import org.apache.ibatis.mapping.SqlCommandType;
|
||||
import org.apache.ibatis.plugin.Interceptor;
|
||||
import top.continew.starter.core.constant.StringConstants;
|
||||
import top.continew.starter.core.exception.BusinessException;
|
||||
import top.continew.starter.security.crypto.annotation.FieldEncrypt;
|
||||
import top.continew.starter.security.crypto.encryptor.IEncryptor;
|
||||
import top.continew.starter.security.crypto.enums.Algorithm;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Parameter;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* 字段解密拦截器
|
||||
@@ -44,9 +31,7 @@ import java.util.stream.Stream;
|
||||
* @author Charles7c
|
||||
* @since 1.4.0
|
||||
*/
|
||||
public abstract class AbstractMyBatisInterceptor implements Interceptor {
|
||||
|
||||
private static final Map<String, Map<String, FieldEncrypt>> ENCRYPT_PARAM_CACHE = new ConcurrentHashMap<>();
|
||||
public abstract class AbstractMyBatisInterceptor {
|
||||
|
||||
/**
|
||||
* 获取所有字符串类型、需要加/解密的、有值字段
|
||||
@@ -58,10 +43,19 @@ public abstract class AbstractMyBatisInterceptor implements Interceptor {
|
||||
if (null == obj) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return Arrays.stream(ReflectUtil.getFields(obj.getClass()))
|
||||
return this.getEncryptFields(obj.getClass());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有字符串类型、需要加/解密的、有值字段
|
||||
*
|
||||
* @param clazz 类型对象
|
||||
* @return 字段列表
|
||||
*/
|
||||
public List<Field> getEncryptFields(Class<?> clazz) {
|
||||
return Arrays.stream(ReflectUtil.getFields(clazz))
|
||||
.filter(field -> String.class.equals(field.getType()))
|
||||
.filter(field -> null != field.getAnnotation(FieldEncrypt.class))
|
||||
.filter(field -> null != ReflectUtil.getFieldValue(obj, field))
|
||||
.toList();
|
||||
}
|
||||
|
||||
@@ -81,115 +75,4 @@ public abstract class AbstractMyBatisInterceptor implements Interceptor {
|
||||
// 使用自定义加/解密处理器
|
||||
return SpringUtil.getBean(encryptorClass);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取加密参数
|
||||
*
|
||||
* @param mappedStatement 映射语句
|
||||
* @return 加密参数
|
||||
*/
|
||||
public Map<String, FieldEncrypt> getEncryptParams(MappedStatement mappedStatement) {
|
||||
return getEncryptParams(mappedStatement, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取加密参数
|
||||
*
|
||||
* @param mappedStatement 映射语句
|
||||
* @param parameterCount 参数数量
|
||||
* @return 加密参数
|
||||
*/
|
||||
public Map<String, FieldEncrypt> getEncryptParams(MappedStatement mappedStatement, Integer parameterCount) {
|
||||
String mappedStatementId = mappedStatement.getId();
|
||||
SqlCommandType sqlCommandType = mappedStatement.getSqlCommandType();
|
||||
if (SqlCommandType.UPDATE != sqlCommandType) {
|
||||
return ENCRYPT_PARAM_CACHE.computeIfAbsent(mappedStatementId, key -> this
|
||||
.getEncryptParams(mappedStatementId, parameterCount));
|
||||
} else {
|
||||
return this.getEncryptParams(mappedStatementId, parameterCount);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取参数名称
|
||||
*
|
||||
* @param parameter 参数
|
||||
* @return 参数名称
|
||||
*/
|
||||
public String getParameterName(Parameter parameter) {
|
||||
Param param = parameter.getAnnotation(Param.class);
|
||||
return null != param ? param.value() : parameter.getName();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取加密参数列表
|
||||
*
|
||||
* @param mappedStatementId 映射语句 ID
|
||||
* @param parameterCount 参数数量
|
||||
* @return 加密参数列表
|
||||
*/
|
||||
private Map<String, FieldEncrypt> getEncryptParams(String mappedStatementId, Integer parameterCount) {
|
||||
Method method = this.getMethod(mappedStatementId, parameterCount);
|
||||
if (method == null) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
return this.getEncryptParams(method);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取映射方法
|
||||
*
|
||||
* @param mappedStatementId 映射语句 ID
|
||||
* @param parameterCount 参数数量
|
||||
* @return 映射方法
|
||||
*/
|
||||
private Method getMethod(String mappedStatementId, Integer parameterCount) {
|
||||
try {
|
||||
String className = CharSequenceUtil.subBefore(mappedStatementId, StringConstants.DOT, true);
|
||||
String wrapperMethodName = CharSequenceUtil.subAfter(mappedStatementId, StringConstants.DOT, true);
|
||||
String methodName = Stream.of("_mpCount", "_COUNT")
|
||||
.filter(wrapperMethodName::endsWith)
|
||||
.findFirst()
|
||||
.map(suffix -> wrapperMethodName.substring(0, wrapperMethodName.length() - suffix.length()))
|
||||
.orElse(wrapperMethodName);
|
||||
// 获取真实方法
|
||||
Optional<Method> methodOptional = Arrays.stream(ReflectUtil.getMethods(Class.forName(className), m -> {
|
||||
if (parameterCount != null) {
|
||||
return Objects.equals(m.getName(), methodName) && m.getParameterCount() == parameterCount;
|
||||
}
|
||||
return Objects.equals(m.getName(), methodName);
|
||||
})).findFirst();
|
||||
return methodOptional.orElse(null);
|
||||
} catch (ClassNotFoundException e) {
|
||||
throw new BusinessException(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取加密参数列表
|
||||
*
|
||||
* @param method 方法
|
||||
* @return 加密参数列表
|
||||
*/
|
||||
private Map<String, FieldEncrypt> getEncryptParams(Method method) {
|
||||
// 获取方法中的加密参数
|
||||
Map<String, FieldEncrypt> map = MapUtil.newHashMap();
|
||||
Parameter[] parameterArr = method.getParameters();
|
||||
for (int i = 0; i < parameterArr.length; i++) {
|
||||
Parameter parameter = parameterArr[i];
|
||||
String parameterName = this.getParameterName(parameter);
|
||||
FieldEncrypt fieldEncrypt = parameter.getAnnotation(FieldEncrypt.class);
|
||||
if (null != fieldEncrypt) {
|
||||
map.put(parameterName, fieldEncrypt);
|
||||
if (String.class.equals(parameter.getType())) {
|
||||
map.put("param" + (i + 1), fieldEncrypt);
|
||||
}
|
||||
} else if (parameterName.startsWith(Constants.ENTITY)) {
|
||||
map.put(parameterName, null);
|
||||
} else if (parameterName.startsWith(Constants.WRAPPER)) {
|
||||
map.put(parameterName, null);
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
}
|
@@ -36,7 +36,7 @@ import java.util.List;
|
||||
* @since 1.4.0
|
||||
*/
|
||||
@Intercepts({@Signature(type = ResultSetHandler.class, method = "handleResultSets", args = {Statement.class})})
|
||||
public class MyBatisDecryptInterceptor extends AbstractMyBatisInterceptor {
|
||||
public class MyBatisDecryptInterceptor extends AbstractMyBatisInterceptor implements Interceptor {
|
||||
|
||||
private CryptoProperties properties;
|
||||
|
||||
@@ -65,6 +65,9 @@ public class MyBatisDecryptInterceptor extends AbstractMyBatisInterceptor {
|
||||
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());
|
||||
|
@@ -20,30 +20,24 @@ import cn.hutool.core.text.CharSequenceUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.core.util.ReflectUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.AbstractWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.Wrapper;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.TableFieldInfo;
|
||||
import com.baomidou.mybatisplus.core.metadata.TableInfo;
|
||||
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Constants;
|
||||
import org.apache.ibatis.cache.CacheKey;
|
||||
import com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor;
|
||||
import org.apache.ibatis.executor.Executor;
|
||||
import org.apache.ibatis.mapping.BoundSql;
|
||||
import org.apache.ibatis.mapping.MappedStatement;
|
||||
import org.apache.ibatis.mapping.SqlCommandType;
|
||||
import org.apache.ibatis.plugin.*;
|
||||
import org.apache.ibatis.session.ResultHandler;
|
||||
import org.apache.ibatis.session.RowBounds;
|
||||
import org.apache.ibatis.type.SimpleTypeRegistry;
|
||||
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;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
import java.sql.SQLException;
|
||||
import java.util.*;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* 字段加密拦截器
|
||||
@@ -51,54 +45,51 @@ import java.util.*;
|
||||
* @author Charles7c
|
||||
* @since 1.4.0
|
||||
*/
|
||||
@Intercepts({@Signature(method = "update", type = Executor.class, args = {MappedStatement.class, Object.class}),
|
||||
@Signature(method = "query", type = Executor.class, args = {MappedStatement.class, Object.class, RowBounds.class,
|
||||
ResultHandler.class, CacheKey.class, BoundSql.class}),
|
||||
@Signature(method = "query", type = Executor.class, args = {MappedStatement.class, Object.class, RowBounds.class,
|
||||
ResultHandler.class})})
|
||||
public class MyBatisEncryptInterceptor extends AbstractMyBatisInterceptor {
|
||||
public class MyBatisEncryptInterceptor extends AbstractMyBatisInterceptor implements InnerInterceptor {
|
||||
|
||||
private CryptoProperties properties;
|
||||
private static final Pattern PARAM_PAIRS_PATTERN = Pattern
|
||||
.compile("#\\{ew\\.paramNameValuePairs\\.(" + Constants.WRAPPER_PARAM + "\\d+)\\}");
|
||||
private final CryptoProperties properties;
|
||||
|
||||
public MyBatisEncryptInterceptor(CryptoProperties properties) {
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
public MyBatisEncryptInterceptor() {
|
||||
@Override
|
||||
public void beforeQuery(Executor executor,
|
||||
MappedStatement mappedStatement,
|
||||
Object parameterObject,
|
||||
RowBounds rowBounds,
|
||||
ResultHandler resultHandler,
|
||||
BoundSql boundSql) {
|
||||
if (null == parameterObject) {
|
||||
return;
|
||||
}
|
||||
if (parameterObject instanceof Map parameterMap) {
|
||||
Set set = new HashSet<>(parameterMap.values());
|
||||
for (Object parameter : set) {
|
||||
if (parameter instanceof AbstractWrapper || parameter instanceof String) {
|
||||
continue;
|
||||
}
|
||||
this.encryptEntity(super.getEncryptFields(parameter), parameter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object intercept(Invocation invocation) throws Throwable {
|
||||
Object[] args = invocation.getArgs();
|
||||
MappedStatement mappedStatement = (MappedStatement)args[0];
|
||||
Object parameter = args[1];
|
||||
if (!this.isEncryptRequired(parameter, mappedStatement.getSqlCommandType())) {
|
||||
return invocation.proceed();
|
||||
public void beforeUpdate(Executor executor,
|
||||
MappedStatement mappedStatement,
|
||||
Object parameterObject) throws SQLException {
|
||||
if (null == parameterObject) {
|
||||
return;
|
||||
}
|
||||
// 使用 @Param 注解的场景
|
||||
if (parameter instanceof HashMap parameterMap) {
|
||||
if (parameterObject instanceof Map parameterMap) {
|
||||
// 带别名方法(使用 @Param 注解的场景)
|
||||
this.encryptMap(parameterMap, mappedStatement);
|
||||
} else {
|
||||
this.doEncrypt(this.getEncryptFields(parameter), parameter);
|
||||
// 无别名方法(例如:MP insert 等方法)
|
||||
this.encryptEntity(super.getEncryptFields(parameterObject), parameterObject);
|
||||
}
|
||||
return invocation.proceed();
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否需要加密处理
|
||||
*
|
||||
* @param parameter 参数
|
||||
* @param sqlCommandType SQL 类型
|
||||
* @return true:是;false:否
|
||||
*/
|
||||
private boolean isEncryptRequired(Object parameter, SqlCommandType sqlCommandType) {
|
||||
if (ObjectUtil.isEmpty(parameter)) {
|
||||
return false;
|
||||
}
|
||||
if (!(SqlCommandType.UPDATE == sqlCommandType || SqlCommandType.INSERT == sqlCommandType || SqlCommandType.SELECT == sqlCommandType)) {
|
||||
return false;
|
||||
}
|
||||
return !SimpleTypeRegistry.isSimpleType(parameter.getClass());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -106,93 +97,93 @@ public class MyBatisEncryptInterceptor extends AbstractMyBatisInterceptor {
|
||||
*
|
||||
* @param parameterMap 参数
|
||||
* @param mappedStatement 映射语句
|
||||
* @throws Exception /
|
||||
*/
|
||||
private void encryptMap(HashMap<String, Object> parameterMap, MappedStatement mappedStatement) throws Exception {
|
||||
Map<String, FieldEncrypt> encryptParamMap = super.getEncryptParams(mappedStatement);
|
||||
if (encryptParamMap.isEmpty() && !parameterMap.isEmpty()) {
|
||||
encryptParamMap = super.getEncryptParams(mappedStatement, parameterMap.size() / 2);
|
||||
private void encryptMap(Map<String, Object> parameterMap, MappedStatement mappedStatement) {
|
||||
Object parameter;
|
||||
// 别名带有 et(针对 MP 的 updateById、update 等方法)
|
||||
if (parameterMap.containsKey(Constants.ENTITY) && null != (parameter = parameterMap.get(Constants.ENTITY))) {
|
||||
this.encryptEntity(super.getEncryptFields(parameter), parameter);
|
||||
}
|
||||
// 别名带有 ew(针对 MP 的 UpdateWrapper、LambdaUpdateWrapper 等参数)
|
||||
if (parameterMap.containsKey(Constants.WRAPPER) && null != (parameter = parameterMap.get(Constants.WRAPPER))) {
|
||||
this.encryptWrapper(parameter, mappedStatement);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理 Wrapper 类型参数加密(针对 MP 的 UpdateWrapper、LambdaUpdateWrapper 等参数)
|
||||
*
|
||||
* @param parameter Wrapper 参数
|
||||
* @param mappedStatement 映射语句
|
||||
* @since 2.1.1
|
||||
* @author cary
|
||||
* @author wangshaopeng@talkweb.com.cn(<a
|
||||
* href="https://blog.csdn.net/tianmaxingkonger/article/details/130986784">基于Mybatis-Plus拦截器实现MySQL数据加解密</a>)
|
||||
*/
|
||||
private void encryptWrapper(Object parameter, MappedStatement mappedStatement) {
|
||||
if (parameter instanceof AbstractWrapper updateWrapper) {
|
||||
String sqlSet = updateWrapper.getSqlSet();
|
||||
if (CharSequenceUtil.isBlank(sqlSet)) {
|
||||
return;
|
||||
}
|
||||
// 将 name=#{ew.paramNameValuePairs.xxx},age=#{ew.paramNameValuePairs.xxx} 切出来
|
||||
String[] elArr = sqlSet.split(StringConstants.COMMA);
|
||||
Map<String, String> propMap = new HashMap<>(elArr.length);
|
||||
Arrays.stream(elArr).forEach(el -> {
|
||||
String[] elPart = el.split(StringConstants.EQUALS);
|
||||
propMap.put(elPart[0], elPart[1]);
|
||||
});
|
||||
// 获取加密字段
|
||||
Class<?> entityClass = mappedStatement.getParameterMap().getType();
|
||||
List<Field> encryptFieldList = super.getEncryptFields(entityClass);
|
||||
for (Field field : encryptFieldList) {
|
||||
FieldEncrypt fieldEncrypt = field.getAnnotation(FieldEncrypt.class);
|
||||
String el = propMap.get(field.getName());
|
||||
if (CharSequenceUtil.isBlank(el)) {
|
||||
continue;
|
||||
}
|
||||
Matcher matcher = PARAM_PAIRS_PATTERN.matcher(el);
|
||||
if (matcher.matches()) {
|
||||
String valueKey = matcher.group(1);
|
||||
Object value = updateWrapper.getParamNameValuePairs().get(valueKey);
|
||||
Object ciphertext;
|
||||
try {
|
||||
ciphertext = this.doEncrypt(value, fieldEncrypt);
|
||||
} catch (Exception e) {
|
||||
throw new BaseException(e);
|
||||
}
|
||||
updateWrapper.getParamNameValuePairs().put(valueKey, ciphertext);
|
||||
}
|
||||
for (Map.Entry<String, FieldEncrypt> encryptParamEntry : encryptParamMap.entrySet()) {
|
||||
String parameterName = encryptParamEntry.getKey();
|
||||
if (parameterName.startsWith(Constants.ENTITY)) {
|
||||
// 兼容 MyBatis Plus 封装的 update 相关方法,updateById、update
|
||||
Object entity = parameterMap.getOrDefault(parameterName, null);
|
||||
this.doEncrypt(this.getEncryptFields(entity), entity);
|
||||
} else if (parameterName.startsWith(Constants.WRAPPER)) {
|
||||
// 处理参数为 Wrapper 的情况
|
||||
Wrapper wrapper = (Wrapper)parameterMap.getOrDefault(parameterName, null);
|
||||
this.doEncrypt(wrapper, mappedStatement);
|
||||
} else {
|
||||
FieldEncrypt fieldEncrypt = encryptParamEntry.getValue();
|
||||
parameterMap.put(parameterName, this.doEncrypt(parameterMap.get(parameterName), fieldEncrypt));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理加密
|
||||
* 处理实体加密
|
||||
*
|
||||
* @param fieldList 加密字段列表
|
||||
* @param entity 实体
|
||||
* @throws Exception /
|
||||
*/
|
||||
private void doEncrypt(List<Field> fieldList, Object entity) throws Exception {
|
||||
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 = encryptor.encrypt(fieldValue.toString(), password, properties.getPublicKey());
|
||||
String ciphertext;
|
||||
try {
|
||||
ciphertext = encryptor.encrypt(fieldValue.toString(), password, properties.getPublicKey());
|
||||
} catch (Exception e) {
|
||||
throw new BaseException(e);
|
||||
}
|
||||
ReflectUtil.setFieldValue(entity, field, ciphertext);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理 Wrapper 加密
|
||||
*
|
||||
* @param wrapper Wrapper 对象
|
||||
* @param mappedStatement 映射语句
|
||||
* @throws Exception /
|
||||
*/
|
||||
private void doEncrypt(Wrapper wrapper, MappedStatement mappedStatement) throws Exception {
|
||||
if (wrapper instanceof AbstractWrapper abstractWrapper) {
|
||||
String sqlSet = abstractWrapper.getSqlSet();
|
||||
if (CharSequenceUtil.isEmpty(sqlSet)) {
|
||||
return;
|
||||
}
|
||||
String className = CharSequenceUtil.subBefore(mappedStatement.getId(), StringConstants.DOT, true);
|
||||
Class<?> mapperClass = Class.forName(className);
|
||||
Optional<Class> baseMapperGenerics = getEntityTypeByMapperClass(mapperClass, Optional.empty());
|
||||
// 获取不到泛型对象 则不进行下面的逻辑
|
||||
if (baseMapperGenerics.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
TableInfo tableInfo = TableInfoHelper.getTableInfo(baseMapperGenerics.get());
|
||||
List<TableFieldInfo> fieldList = tableInfo.getFieldList();
|
||||
// 将 name=#{ew.paramNameValuePairs.xxx},age=#{ew.paramNameValuePairs.xxx} 切出来
|
||||
for (String sqlFragment : sqlSet.split(Constants.COMMA)) {
|
||||
String columnName = sqlFragment.split(Constants.EQUALS)[0];
|
||||
// 截取其中的 xxx 字符,例如:#{ew.paramNameValuePairs.xxx}
|
||||
String paramNameVal = sqlFragment.split(Constants.EQUALS)[1].substring(25, sqlFragment
|
||||
.split(Constants.EQUALS)[1].length() - 1);
|
||||
Optional<TableFieldInfo> fieldInfo = fieldList.stream()
|
||||
.filter(f -> f.getColumn().equals(columnName))
|
||||
.findAny();
|
||||
if (fieldInfo.isPresent()) {
|
||||
TableFieldInfo tableFieldInfo = fieldInfo.get();
|
||||
FieldEncrypt fieldEncrypt = tableFieldInfo.getField().getAnnotation(FieldEncrypt.class);
|
||||
if (fieldEncrypt != null) {
|
||||
Map<String, Object> paramNameValuePairs = abstractWrapper.getParamNameValuePairs();
|
||||
paramNameValuePairs.put(paramNameVal, this.doEncrypt(paramNameValuePairs
|
||||
.get(paramNameVal), fieldEncrypt));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理加密
|
||||
*
|
||||
@@ -209,40 +200,4 @@ public class MyBatisEncryptInterceptor extends AbstractMyBatisInterceptor {
|
||||
String password = ObjectUtil.defaultIfBlank(fieldEncrypt.password(), properties.getPassword());
|
||||
return encryptor.encrypt(parameterValue.toString(), password, properties.getPublicKey());
|
||||
}
|
||||
|
||||
/**
|
||||
* 从 Mapper 获取泛型
|
||||
*
|
||||
* @param mapperClass Mapper class
|
||||
* @param tempResult 临时存储的泛型对象
|
||||
* @return 泛型
|
||||
*/
|
||||
private static Optional<Class> getEntityTypeByMapperClass(Class<?> mapperClass, Optional<Class> tempResult) {
|
||||
Type[] genericInterfaces = mapperClass.getGenericInterfaces();
|
||||
Optional<Class> result = tempResult;
|
||||
for (Type genericInterface : genericInterfaces) {
|
||||
if (genericInterface instanceof ParameterizedType parameterizedType) {
|
||||
Type rawType = parameterizedType.getRawType();
|
||||
Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
|
||||
// 如果匹配上 BaseMapper 且泛型参数是 Class 类型,则直接返回
|
||||
if (rawType.equals(BaseMapper.class)) {
|
||||
return actualTypeArguments[0] instanceof Class
|
||||
? Optional.of((Class)actualTypeArguments[0])
|
||||
: result;
|
||||
} else if (rawType instanceof Class interfaceClass) {
|
||||
// 如果泛型参数是 Class 类型,则传递给递归调用
|
||||
if (actualTypeArguments[0] instanceof Class tempResultClass) {
|
||||
result = Optional.of(tempResultClass);
|
||||
}
|
||||
// 递归调用,继续查找
|
||||
Optional<Class> innerResult = getEntityTypeByMapperClass(interfaceClass, result);
|
||||
if (innerResult.isPresent()) {
|
||||
return innerResult;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// 如果没有找到,返回传递进来的 tempResult
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user