mirror of
https://github.com/continew-org/continew-starter.git
synced 2025-09-09 20:57:23 +08:00
fix: 修复单参查询加密失效的问题
This commit is contained in:
@@ -16,14 +16,23 @@
|
|||||||
|
|
||||||
package top.continew.starter.security.crypto.core;
|
package top.continew.starter.security.crypto.core;
|
||||||
|
|
||||||
|
import cn.hutool.core.text.CharSequenceUtil;
|
||||||
import cn.hutool.core.util.ReflectUtil;
|
import cn.hutool.core.util.ReflectUtil;
|
||||||
import cn.hutool.extra.spring.SpringUtil;
|
import cn.hutool.extra.spring.SpringUtil;
|
||||||
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
import org.apache.ibatis.mapping.MappedStatement;
|
||||||
|
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.encryptor.IEncryptor;
|
import top.continew.starter.security.crypto.encryptor.IEncryptor;
|
||||||
import top.continew.starter.security.crypto.enums.Algorithm;
|
import top.continew.starter.security.crypto.enums.Algorithm;
|
||||||
|
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.lang.reflect.Parameter;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 字段解密拦截器
|
* 字段解密拦截器
|
||||||
@@ -33,13 +42,16 @@ import java.util.*;
|
|||||||
*/
|
*/
|
||||||
public abstract class AbstractMyBatisInterceptor {
|
public abstract class AbstractMyBatisInterceptor {
|
||||||
|
|
||||||
|
private static final Map<Class<?>, List<Field>> CLASS_FIELD_CACHE = new ConcurrentHashMap<>();
|
||||||
|
private static final Map<String, Map<String, FieldEncrypt>> ENCRYPT_PARAM_CACHE = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取所有字符串类型、需要加/解密的、有值字段
|
* 获取所有字符串类型、需要加/解密的、有值字段
|
||||||
*
|
*
|
||||||
* @param obj 对象
|
* @param obj 对象
|
||||||
* @return 字段列表
|
* @return 字段列表
|
||||||
*/
|
*/
|
||||||
public List<Field> getEncryptFields(Object obj) {
|
protected List<Field> getEncryptFields(Object obj) {
|
||||||
if (null == obj) {
|
if (null == obj) {
|
||||||
return Collections.emptyList();
|
return Collections.emptyList();
|
||||||
}
|
}
|
||||||
@@ -52,11 +64,11 @@ public abstract class AbstractMyBatisInterceptor {
|
|||||||
* @param clazz 类型对象
|
* @param clazz 类型对象
|
||||||
* @return 字段列表
|
* @return 字段列表
|
||||||
*/
|
*/
|
||||||
public List<Field> getEncryptFields(Class<?> clazz) {
|
protected List<Field> getEncryptFields(Class<?> clazz) {
|
||||||
return Arrays.stream(ReflectUtil.getFields(clazz))
|
return CLASS_FIELD_CACHE.computeIfAbsent(clazz, key -> Arrays.stream(ReflectUtil.getFields(clazz))
|
||||||
.filter(field -> String.class.equals(field.getType()))
|
.filter(field -> String.class.equals(field.getType()))
|
||||||
.filter(field -> null != field.getAnnotation(FieldEncrypt.class))
|
.filter(field -> null != field.getAnnotation(FieldEncrypt.class))
|
||||||
.toList();
|
.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -65,7 +77,7 @@ public abstract class AbstractMyBatisInterceptor {
|
|||||||
* @param fieldEncrypt 字段加密注解
|
* @param fieldEncrypt 字段加密注解
|
||||||
* @return 加/解密处理器
|
* @return 加/解密处理器
|
||||||
*/
|
*/
|
||||||
public IEncryptor getEncryptor(FieldEncrypt fieldEncrypt) {
|
protected IEncryptor getEncryptor(FieldEncrypt fieldEncrypt) {
|
||||||
Class<? extends IEncryptor> encryptorClass = fieldEncrypt.encryptor();
|
Class<? extends IEncryptor> encryptorClass = fieldEncrypt.encryptor();
|
||||||
// 使用预定义加/解密处理器
|
// 使用预定义加/解密处理器
|
||||||
if (encryptorClass == IEncryptor.class) {
|
if (encryptorClass == IEncryptor.class) {
|
||||||
@@ -75,4 +87,63 @@ public abstract class AbstractMyBatisInterceptor {
|
|||||||
// 使用自定义加/解密处理器
|
// 使用自定义加/解密处理器
|
||||||
return SpringUtil.getBean(encryptorClass);
|
return SpringUtil.getBean(encryptorClass);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取加密参数
|
||||||
|
*
|
||||||
|
* @param mappedStatement 映射语句
|
||||||
|
* @return 获取加密参数
|
||||||
|
*/
|
||||||
|
protected Map<String, FieldEncrypt> getEncryptParameters(MappedStatement mappedStatement) {
|
||||||
|
String mappedStatementId = mappedStatement.getId();
|
||||||
|
return ENCRYPT_PARAM_CACHE.computeIfAbsent(mappedStatementId, key -> {
|
||||||
|
Method method = this.getMethod(mappedStatementId);
|
||||||
|
if (null == method) {
|
||||||
|
return Collections.emptyMap();
|
||||||
|
}
|
||||||
|
Map<String, FieldEncrypt> encryptMap = new HashMap<>();
|
||||||
|
Parameter[] parameters = method.getParameters();
|
||||||
|
for (int i = 0; i < parameters.length; i++) {
|
||||||
|
Parameter parameter = parameters[i];
|
||||||
|
FieldEncrypt fieldEncrypt = parameter.getAnnotation(FieldEncrypt.class);
|
||||||
|
if (null == fieldEncrypt) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
String parameterName = this.getParameterName(parameter);
|
||||||
|
encryptMap.put(parameterName, fieldEncrypt);
|
||||||
|
if (String.class.equals(parameter.getType())) {
|
||||||
|
encryptMap.put("param" + (i + 1), fieldEncrypt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return encryptMap;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取映射方法
|
||||||
|
*
|
||||||
|
* @param mappedStatementId 映射语句 ID
|
||||||
|
* @return 映射方法
|
||||||
|
*/
|
||||||
|
private Method getMethod(String mappedStatementId) {
|
||||||
|
String className = CharSequenceUtil.subBefore(mappedStatementId, StringConstants.DOT, true);
|
||||||
|
String methodName = CharSequenceUtil.subAfter(mappedStatementId, StringConstants.DOT, true);
|
||||||
|
try {
|
||||||
|
Method[] methods = ReflectUtil.getMethods(Class.forName(className));
|
||||||
|
return Stream.of(methods).filter(method -> method.getName().equals(methodName)).findFirst().orElse(null);
|
||||||
|
} catch (ClassNotFoundException e) {
|
||||||
|
throw new BaseException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取参数名称
|
||||||
|
*
|
||||||
|
* @param parameter 参数
|
||||||
|
* @return 参数名称
|
||||||
|
*/
|
||||||
|
public String getParameterName(Parameter parameter) {
|
||||||
|
Param param = parameter.getAnnotation(Param.class);
|
||||||
|
return null != param ? param.value() : parameter.getName();
|
||||||
|
}
|
||||||
}
|
}
|
@@ -17,6 +17,7 @@
|
|||||||
package top.continew.starter.security.crypto.core;
|
package top.continew.starter.security.crypto.core;
|
||||||
|
|
||||||
import cn.hutool.core.text.CharSequenceUtil;
|
import cn.hutool.core.text.CharSequenceUtil;
|
||||||
|
import cn.hutool.core.util.ClassUtil;
|
||||||
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 com.baomidou.mybatisplus.core.conditions.AbstractWrapper;
|
import com.baomidou.mybatisplus.core.conditions.AbstractWrapper;
|
||||||
@@ -34,7 +35,6 @@ import top.continew.starter.security.crypto.autoconfigure.CryptoProperties;
|
|||||||
import top.continew.starter.security.crypto.encryptor.IEncryptor;
|
import top.continew.starter.security.crypto.encryptor.IEncryptor;
|
||||||
|
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.sql.SQLException;
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
@@ -66,20 +66,12 @@ public class MyBatisEncryptInterceptor extends AbstractMyBatisInterceptor implem
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (parameterObject instanceof Map parameterMap) {
|
if (parameterObject instanceof Map parameterMap) {
|
||||||
Set set = new HashSet<>(parameterMap.values());
|
this.encryptQueryParameter(parameterMap, mappedStatement);
|
||||||
for (Object parameter : set) {
|
|
||||||
if (parameter instanceof AbstractWrapper || parameter instanceof String) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
this.encryptEntity(super.getEncryptFields(parameter), parameter);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void beforeUpdate(Executor executor,
|
public void beforeUpdate(Executor executor, MappedStatement mappedStatement, Object parameterObject) {
|
||||||
MappedStatement mappedStatement,
|
|
||||||
Object parameterObject) throws SQLException {
|
|
||||||
if (null == parameterObject) {
|
if (null == parameterObject) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -106,12 +98,39 @@ public class MyBatisEncryptInterceptor extends AbstractMyBatisInterceptor implem
|
|||||||
}
|
}
|
||||||
// 别名带有 ew(针对 MP 的 UpdateWrapper、LambdaUpdateWrapper 等参数)
|
// 别名带有 ew(针对 MP 的 UpdateWrapper、LambdaUpdateWrapper 等参数)
|
||||||
if (parameterMap.containsKey(Constants.WRAPPER) && null != (parameter = parameterMap.get(Constants.WRAPPER))) {
|
if (parameterMap.containsKey(Constants.WRAPPER) && null != (parameter = parameterMap.get(Constants.WRAPPER))) {
|
||||||
this.encryptWrapper(parameter, mappedStatement);
|
this.encryptUpdateWrapper(parameter, mappedStatement);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 处理 Wrapper 类型参数加密(针对 MP 的 UpdateWrapper、LambdaUpdateWrapper 等参数)
|
* 加密查询参数(针对 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 (null != fieldEncrypt) {
|
||||||
|
parameterMap.put(parameterName, this.doEncrypt(str, fieldEncrypt));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 实体参数
|
||||||
|
this.encryptEntity(super.getEncryptFields(parameterValue), parameterValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理 UpdateWrapper 类型参数加密(针对 MP 的 UpdateWrapper、LambdaUpdateWrapper 等参数)
|
||||||
*
|
*
|
||||||
* @param parameter Wrapper 参数
|
* @param parameter Wrapper 参数
|
||||||
* @param mappedStatement 映射语句
|
* @param mappedStatement 映射语句
|
||||||
@@ -120,7 +139,7 @@ public class MyBatisEncryptInterceptor extends AbstractMyBatisInterceptor implem
|
|||||||
* @author wangshaopeng@talkweb.com.cn(<a
|
* @author wangshaopeng@talkweb.com.cn(<a
|
||||||
* href="https://blog.csdn.net/tianmaxingkonger/article/details/130986784">基于Mybatis-Plus拦截器实现MySQL数据加解密</a>)
|
* href="https://blog.csdn.net/tianmaxingkonger/article/details/130986784">基于Mybatis-Plus拦截器实现MySQL数据加解密</a>)
|
||||||
*/
|
*/
|
||||||
private void encryptWrapper(Object parameter, MappedStatement mappedStatement) {
|
private void encryptUpdateWrapper(Object parameter, MappedStatement mappedStatement) {
|
||||||
if (parameter instanceof AbstractWrapper updateWrapper) {
|
if (parameter instanceof AbstractWrapper updateWrapper) {
|
||||||
String sqlSet = updateWrapper.getSqlSet();
|
String sqlSet = updateWrapper.getSqlSet();
|
||||||
if (CharSequenceUtil.isBlank(sqlSet)) {
|
if (CharSequenceUtil.isBlank(sqlSet)) {
|
||||||
@@ -146,12 +165,7 @@ public class MyBatisEncryptInterceptor extends AbstractMyBatisInterceptor implem
|
|||||||
if (matcher.matches()) {
|
if (matcher.matches()) {
|
||||||
String valueKey = matcher.group(1);
|
String valueKey = matcher.group(1);
|
||||||
Object value = updateWrapper.getParamNameValuePairs().get(valueKey);
|
Object value = updateWrapper.getParamNameValuePairs().get(valueKey);
|
||||||
Object ciphertext;
|
Object ciphertext = this.doEncrypt(value, fieldEncrypt);
|
||||||
try {
|
|
||||||
ciphertext = this.doEncrypt(value, fieldEncrypt);
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new BaseException(e);
|
|
||||||
}
|
|
||||||
updateWrapper.getParamNameValuePairs().put(valueKey, ciphertext);
|
updateWrapper.getParamNameValuePairs().put(valueKey, ciphertext);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -189,15 +203,18 @@ public class MyBatisEncryptInterceptor extends AbstractMyBatisInterceptor implem
|
|||||||
*
|
*
|
||||||
* @param parameterValue 参数值
|
* @param parameterValue 参数值
|
||||||
* @param fieldEncrypt 字段加密注解
|
* @param fieldEncrypt 字段加密注解
|
||||||
* @throws Exception /
|
|
||||||
*/
|
*/
|
||||||
private Object doEncrypt(Object parameterValue, FieldEncrypt fieldEncrypt) throws Exception {
|
private Object doEncrypt(Object parameterValue, FieldEncrypt fieldEncrypt) {
|
||||||
if (null == parameterValue) {
|
if (null == parameterValue) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
IEncryptor encryptor = super.getEncryptor(fieldEncrypt);
|
IEncryptor encryptor = super.getEncryptor(fieldEncrypt);
|
||||||
// 优先获取自定义对称加密算法密钥,获取不到时再获取全局配置
|
// 优先获取自定义对称加密算法密钥,获取不到时再获取全局配置
|
||||||
String password = ObjectUtil.defaultIfBlank(fieldEncrypt.password(), properties.getPassword());
|
String password = ObjectUtil.defaultIfBlank(fieldEncrypt.password(), properties.getPassword());
|
||||||
return encryptor.encrypt(parameterValue.toString(), password, properties.getPublicKey());
|
try {
|
||||||
|
return encryptor.encrypt(parameterValue.toString(), password, properties.getPublicKey());
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new BaseException(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user