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))。

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

相关推荐
梦想平凡11 分钟前
PHP 微信棋牌开发全解析:高级教程
android·数据库·oracle
TianyaOAO21 分钟前
mysql的事务控制和数据库的备份和恢复
数据库·mysql
Ewen Seong33 分钟前
mysql系列5—Innodb的缓存
数据库·mysql·缓存
码农老起1 小时前
企业如何通过TDSQL实现高效数据库迁移与性能优化
数据库·性能优化
夏木~2 小时前
Oracle 中什么情况下 可以使用 EXISTS 替代 IN 提高查询效率
数据库·oracle
W21552 小时前
Liunx下MySQL:表的约束
数据库·mysql
黄名富2 小时前
Redis 附加功能(二)— 自动过期、流水线与事务及Lua脚本
java·数据库·redis·lua
言、雲2 小时前
从tryLock()源码来出发,解析Redisson的重试机制和看门狗机制
java·开发语言·数据库
一个程序员_zhangzhen3 小时前
sqlserver新建用户并分配对视图的只读权限
数据库·sqlserver
zfj3213 小时前
学技术学英文:代码中的锁:悲观锁和乐观锁
数据库·乐观锁··悲观锁·竞态条件