项目场景:
提示:这里简述项目相关背景:
例如:Mybatis加密解密查询操作(sql前),where要传入加密后的字段时遇到的问题
问题描述
提示:这里描述项目中遇到的问题:
例如:数据传输过程中数据不时出现丢失的情况,偶尔会丢失一部分数据
APP 中接收数据代码:
public Object intercept(Invocation invocation) throws Throwable {ParameterHandler parameterHandler = (ParameterHandler) invocation.getTarget();// 获取传入的参数对象Object parameterObject = parameterHandler.getParameterObject();if (parameterObject instanceof Map) {Map<?, ?> paramMap = (Map<?, ?>) parameterObject;for (Map.Entry<?, ?> entry : paramMap.entrySet()) {Object key = entry.getKey();Object value = entry.getValue();// 对于带有 @Sensitive 注解的字段,进行加密处理if (value != null && !value.toString().isEmpty()) { // 判空和空字符串检查// 反射检查该字段是否标记了 @Sensitive 注解if (isSensitiveField(value)) {String encryptedValue = SensitiveDataUtils.encrypt(value.toString(), SM4_KEY);((Map<Object, Object>) paramMap).put(key, encryptedValue);}}}} else {// 处理非 Map 类型的参数(如单个对象查询)encryptSensitiveFields(parameterObject);}// 执行原始逻辑return invocation.proceed();}
这个方法有问题啊
原因分析:
提示:这里填写问题的分析:
是的,这个 intercept 方法存在几个潜在问题,可能导致在处理加密字段时出现错误。以下是详细的分析和改进建议:
- parameterObject 为 Map 类型时的处理
在你当前的代码中,如果 parameterObject 是 Map 类型,会迭代其中的 key-value 对并检查每个 value 是否标记了 @Sensitive 注解,进而进行加密处理。这里有几个潜在问题:
value 可能是复杂对象:目前的判断条件是 if (value != null && !value.toString().isEmpty()),但是 value 可能是一个复杂对象(例如 User 对象),而不是一个简单的 String 类型。在这种情况下,直接使用 value.toString() 可能并不能反映实际字段的内容。
isSensitiveField 反射问题:你尝试通过反射检查 value 是否包含 @Sensitive 注解,但是 value 很可能是一个普通的 Java 对象(如 AdminUserDO),直接传给 isSensitiveField 可能会导致反射错误。
-
parameterObject 不是 Map 类型时的处理
如果 parameterObject 不是 Map 类型,你使用 encryptSensitiveFields(parameterObject) 来处理加密。该方法会检查对象中的字段并加密被 @Sensitive 注解标记的字段。问题在于,你并没有考虑 parameterObject 可能是一个嵌套对象,或者该对象的类型可能没有适当的 getter 方法,导致反射出错。 -
改进建议
我们需要确保在处理 Map 类型和非 Map 类型的参数时,都能正确处理嵌套对象和字段加密。
解决方案:
提示:这里填写该问题的具体解决方案:
package cn.iocoder.yudao.framework.mybatis.core.handler;import cn.iocoder.yudao.framework.annotation.Sensitive;
import cn.iocoder.yudao.framework.mybatis.core.util.SensitiveDataUtils;
import org.apache.ibatis.executor.parameter.ParameterHandler;
import org.apache.ibatis.plugin.*;import java.lang.reflect.Field;
import java.sql.PreparedStatement;
import java.util.Map;import static cn.iocoder.yudao.framework.mybatis.core.util.SensitiveDataUtils.SM4_KEY;@Intercepts({@Signature(type = ParameterHandler.class, method = "setParameters", args = {PreparedStatement.class})
})
public class SensitiveDataEncryptInterceptor implements Interceptor {@Overridepublic Object intercept(Invocation invocation) throws Throwable {ParameterHandler parameterHandler = (ParameterHandler) invocation.getTarget();// 获取传入的参数对象Object parameterObject = parameterHandler.getParameterObject();// 如果是 Map 类型的参数if (parameterObject instanceof Map) {Map<?, ?> paramMap = (Map<?, ?>) parameterObject;for (Map.Entry<?, ?> entry : paramMap.entrySet()) {Object key = entry.getKey();Object value = entry.getValue();// 判断 value 是否为带有 @Sensitive 注解的字段,进行加密处理if (value != null) {// 对 value 进行加密String encryptedValue = encryptSensitiveValue(value);// 如果字段需要加密,替换 Map 中的值if (encryptedValue != null) {// 强制转换为 Map<Object, Object> 来避免类型问题((Map<Object, Object>) paramMap).put(key, encryptedValue);}}}} else {// 如果不是 Map 类型,处理对象的字段加密encryptSensitiveFields(parameterObject);}// 执行原始逻辑return invocation.proceed();}private String encryptSensitiveValue(Object value) throws Exception {if (value instanceof String && isSensitiveField(value)) {// 对象字段加密处理return SensitiveDataUtils.encrypt(value.toString(), SM4_KEY);}return null;}private boolean isSensitiveField(Object value) {// 判断该对象字段是否带有 @Sensitive 注解Class<?> clazz = value.getClass();for (Field field : clazz.getDeclaredFields()) {if (field.isAnnotationPresent(Sensitive.class)) {return true; // 该字段带有 @Sensitive 注解}}return false;}private void encryptSensitiveFields(Object parameterObject) throws Exception {// 遍历对象的字段,检查是否带有 @Sensitive 注解Field[] fields = parameterObject.getClass().getDeclaredFields();for (Field field : fields) {if (field.isAnnotationPresent(Sensitive.class)) {field.setAccessible(true);Object value = field.get(parameterObject);if (value != null) {String encryptedValue = SensitiveDataUtils.encrypt(value.toString(), SM4_KEY);field.set(parameterObject, encryptedValue);}}}}
}
主要改进点:
encryptSensitiveValue 方法:
这个方法首先检查传入的 value 是否是需要加密的字段,如果是,则进行加密。
通过调用 isSensitiveField(value) 来判断是否需要加密,而不直接依赖 value.toString()。
isSensitiveField 方法:
通过反射检查 value 对象是否包含带有 @Sensitive 注解的字段。之前可能存在错误的反射逻辑,这里是基于对象字段的反射检查。
encryptSensitiveFields 方法:
遍历 parameterObject 的字段,并加密带有 @Sensitive 注解的字段。处理对象字段时,直接通过反射获取字段并进行加密。
其他注意事项:
确保 SensitiveDataUtils.encrypt 方法能够正确处理所有类型的 value,尤其是对于非 String 类型的对象,可能需要进一步优化加密处理。
如果 parameterObject 是一个复杂的对象,确保该对象的所有字段都可以通过反射访问(即字段为 public 或者使用了 setAccessible(true))。
这样修改后,应该能够有效避免反射错误并正确处理加密逻辑。