Mybatis加密解密查询操作(sql前),where要传入加密后的字段时遇到的问题

项目场景:

提示:这里简述项目相关背景:

例如:Mybatis加密解密查询操作(sql前),where要传入加密后的字段时遇到的问题


问题描述

提示:这里描述项目中遇到的问题:

例如:数据传输过程中数据不时出现丢失的情况,偶尔会丢失一部分数据

APP 中接收数据代码:

c 复制代码
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 方法存在几个潜在问题,可能导致在处理加密字段时出现错误。以下是详细的分析和改进建议:

  1. 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 可能会导致反射错误。

  1. parameterObject 不是 Map 类型时的处理

    如果 parameterObject 不是 Map 类型,你使用 encryptSensitiveFields(parameterObject) 来处理加密。该方法会检查对象中的字段并加密被 @Sensitive 注解标记的字段。问题在于,你并没有考虑 parameterObject 可能是一个嵌套对象,或者该对象的类型可能没有适当的 getter 方法,导致反射出错。

  2. 改进建议

    我们需要确保在处理 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 {

    @Override
    public 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))。

这样修改后,应该能够有效避免反射错误并正确处理加密逻辑。

相关推荐
struggle20251 小时前
RushDB开源程序 是现代应用程序和 AI 的即时数据库。建立在 Neo4j 之上
数据库·typescript·neo4j
伤不起bb3 小时前
Redis 哨兵模式
数据库·redis·缓存
卑微的Coder3 小时前
Redis Set集合命令、内部编码及应用场景(详细)
java·数据库·redis
2501_915373883 小时前
Redis线程安全深度解析:单线程模型的并发智慧
数据库·redis·安全
呼拉拉呼拉3 小时前
Redis知识体系
数据库·redis·缓存·知识体系
霖檬ing3 小时前
Redis——主从&哨兵配置
数据库·redis·缓存
卜及中6 小时前
【Redis/2】核心特性、应用场景与安装配置
数据库·redis·缓存
LucianaiB6 小时前
如何做好一份优秀的技术文档:专业指南与最佳实践
android·java·数据库
Eiceblue6 小时前
Python读取PDF:文本、图片与文档属性
数据库·python·pdf
在未来等你9 小时前
SQL进阶之旅 Day 21:临时表与内存表应用
sql·mysql·postgresql·database·temporary-table·memory-table·sql-optimization