mybatis-plus 基于拦截器数据加密新增成功,修改失败原因

mybatis基于拦截器对敏感数据加密

类注解


import java.lang.annotation.*;

/**
 * BUG不找我
 * 敏感信息类的注解
 * @author huatao
 * @date 2023/12/29  19:40
 */
@Inherited
@Target({ ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
public @interface SensitiveData {
}

     敏感字段注解

import java.lang.annotation.*;

/**
 * BUG不找我
 * 注解敏感信息类中敏感字段的注解
 * @author huatao
 * @date 2023/12/29  19:41
 */
@Inherited
@Target({ ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
public @interface SensitiveField {
}

AES对称加密


import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.symmetric.AES;
import system.dependent.data.encryptio.annotate.SensitiveField;

import java.lang.reflect.Field;
import java.util.Objects;

/**
 * BUG不找我
 * AES加密类
 * @author huatao
 * @date 2023/12/30  9:39
 */
public class AesUtils {
    private final static String KEY="mybatis.interceptor.keys";//加解密key值
    private final static AES aesUtils= SecureUtil.aes(KEY.getBytes());//加密对象

    /**
     * 加密
     * @param declaredFields
     * @param paramsObject
     * @param <T>
     * @return
     * @throws Exception
     */
    public static  <T> T encrypt(Field[] declaredFields, T paramsObject) throws Exception {
        for (Field field : declaredFields) {
            //取出所有被EncryptDecryptField注解的字段
            SensitiveField sensitiveField = field.getAnnotation(SensitiveField.class);
            //判断是否为空
            if (!Objects.isNull(sensitiveField)) {
                //反射设置允许获取私有属性
                field.setAccessible(true);
                //获取对象属性
                Object object = field.get(paramsObject);
                //暂时只实现String类型的加密
                if (object instanceof String) {
                    String value = (String) object;
                    //加密数据
                    byte[] encrypt = aesUtils.encrypt(value);
                    //加密  这里我使用自定义的AES加密工具
                    field.set(paramsObject,  aesUtils.encryptHex(encrypt));
                }
            }
        }
        return paramsObject;
    }

    /**
     * 解密
     * @param result
     * @param <T>
     * @return
     * @throws Exception
     */
    public static  <T> T decrypt(T result) throws Exception {
        //取出resultType的类
        Class<?> resultClass = result.getClass();
        Field[] declaredFields = resultClass.getDeclaredFields();
        for (Field field : declaredFields) {
            //取出所有被EncryptDecryptField注解的字段
            SensitiveField sensitiveField = field.getAnnotation(SensitiveField.class);
            if (!Objects.isNull(sensitiveField)) {
                field.setAccessible(true);
                Object object = field.get(result);
                //只支持String的解密
                if (object instanceof String) {
                    String value = (String) object;
                    //对注解的字段进行逐一解密
                    //对于纯数字字符不解密
                    field.set(result, aesUtils.decryptStr(value));
                }
            }
        }
        return result;
    }

}

解密类


/**
 * BUG不找我
 *  出参解密拦截器
 * @author huatao
 * @date 2023/12/30  9:02
 */
@Component
@Intercepts(
        @Signature(type = ResultSetHandler.class,method = "handleResultSets",args = {Statement.class})
)
public class DecryptInterceptor  implements Interceptor {
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        //取出查询的结果
        Object resultObject = invocation.proceed();
        if (Objects.isNull(resultObject)) {
            return null;
        }
        //基于selectList
        if (resultObject instanceof ArrayList) {
            ArrayList resultList = (ArrayList) resultObject;
            if (!CollectionUtils.isEmpty(resultList) && needToDecrypt(resultList.get(0))) {
                for (Object result : resultList) {
                    //逐一解密
                    AesUtils.decrypt(result);
                }
            }
            //基于selectOne
        } else {
            if (needToDecrypt(resultObject)) {
                AesUtils.decrypt(resultObject);
            }
        }
        return resultObject;
    }

    private boolean needToDecrypt(Object object) {
        Class<?> objectClass = object.getClass();
        SensitiveData sensitiveData = AnnotationUtils.findAnnotation(objectClass, SensitiveData.class);
        return Objects.nonNull(sensitiveData);
    }
    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }

    @Override
    public void setProperties(Properties properties) {
    }
}

加密类


import org.apache.ibatis.binding.MapperMethod;
import org.apache.ibatis.executor.parameter.ParameterHandler;
import org.apache.ibatis.plugin.*;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Component;
import system.dependent.data.encryptio.annotate.SensitiveData;
import system.dependent.data.encryptio.utils.AesUtils;

import java.lang.reflect.Field;
import java.sql.PreparedStatement;
import java.util.Map;
import java.util.Objects;
import java.util.Properties;


/**
 * BUG不找我
 * 入参加密拦截器
 * @author huatao
 * @date 2023/11/11  14:45
 */

@Component
@Intercepts({
        @Signature(type = ParameterHandler.class, method = "setParameters", args = PreparedStatement.class),
})
public class EncryptInterceptor implements Interceptor {

    private final AESManager aesManager;

    @Autowired
    public EncryptInterceptor(AESManager aesManager) {
        this.aesManager = aesManager;
    }

    public Object intercept(Invocation invocation) throws Throwable {
        //@Signature 指定了 type= parameterHandler 后,这里的 invocation.getTarget() 便是parameterHandler
        //若指定ResultSetHandler ,这里则能强转为ResultSetHandler
        ParameterHandler parameterHandler = (ParameterHandler) invocation.getTarget();
        // 获取参数对像,即 mapper 中 paramsType 的实例
        Field parameterField = parameterHandler.getClass().getDeclaredField("parameterObject");
        parameterField.setAccessible(true);
        //取出实例,当parameterObject 是单独一个对象时
        Object parameterObject = parameterField.get(parameterHandler);
        if (parameterObject != null) {
            Class<?> parameterObjectClass = parameterObject.getClass();
            //校验该实例的类是否被@SensitiveData所注解
            SensitiveData sensitiveData = AnnotationUtils.findAnnotation(parameterObjectClass, SensitiveData.class);
            if (Objects.nonNull(sensitiveData)) {
                //取出当前当前类所有字段,传入加密方法
                Field[] declaredFields = parameterObjectClass.getDeclaredFields();
                aesManager.encrypt(declaredFields, parameterObject);
            }
        }
        return invocation.proceed();
    }

    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }

    @Override
    public void setProperties(Properties properties) {

    }

}

mybatis-plus新增和修改差异 

       mybatis入参加密以及查询解密到这里就结束了,但是mybatis和mybatis-plus有点差异。

        我们一般写mybatis新增和修改时mapper层方法如下


import office.automation.bean.po.UserInfo ;
import office.automation.mapper.base.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;

/**
 * BUG不找我
 * 用户数据访问层
 * @author huatao
 * @date 2023/10/31  14:57
 */
@Mapper
public interface UserInfoMapper  extends BaseMapper<AuthorityUserInfoPO> {

    /**
     * 新增用户
     * @param userInfo
     * @return
     */
    int addUserInfo(UserInfo userInfo);

    /**
     * 修改用户
     * @param userInfo
     * @return
     */
    int updateUserInfo(UserInfo userInfo);
    
}

       mybatis-plus 底层封装的新增和修改如下

 根据上面代码可以看出来myabtis-plus修改方法多了一个@Param注解,这个就是我们为什么新增数据时敏感数据可以加密成功,而修改却不行,下面是我们新增和修改使用mybatis-plus拦截器拦截时获取到的数据

新增

 修改

从截图可以看出来,我们新增时获取到入参直接就是实体类,但是修改时获取到的是一个ParamMap,这是因为前面说过的mybatis-plus入参时有一个@Param注解 ,导致我们不能直接获取到我们具体的数据实体类,所以使用mybatis-plus时加密类需要修改成如下

mybatis-plus加密类

import org.apache.ibatis.binding.MapperMethod;
import org.apache.ibatis.executor.parameter.ParameterHandler;
import org.apache.ibatis.plugin.*;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Component;
import system.dependent.data.encryptio.annotate.SensitiveData;
import system.dependent.data.encryptio.utils.AesUtils;

import java.lang.reflect.Field;
import java.sql.PreparedStatement;
import java.util.Map;
import java.util.Objects;
import java.util.Properties;

/**
 * BUG不找我
 * 入参加密拦截器
 * @author huatao
 * @date 2023/12/30  9:17
 */
@Component
@Intercepts({@Signature(type = ParameterHandler.class, method = "setParameters", args = PreparedStatement.class),})
public class EncryptInterceptor implements Interceptor {


    public Object intercept(Invocation invocation) throws Throwable {
        //@Signature 指定了 type= parameterHandler 后,这里的 invocation.getTarget() 便是parameterHandler
        //若指定ResultSetHandler ,这里则能强转为ResultSetHandler
        ParameterHandler parameterHandler = (ParameterHandler) invocation.getTarget();
        // 获取参数对像,即 mapper 中 paramsType 的实例
        Field parameterField = parameterHandler.getClass().getDeclaredField("parameterObject");
        parameterField.setAccessible(true);
        //取出实例,当parameterObject 是单独一个对象时
        Object parameterObject = parameterField.get(parameterHandler);
        if (parameterObject != null) {
            Class<?> parameterObjectClass =null;
            //判断入参是否为map类型
            if(parameterObject instanceof MapperMethod.ParamMap ){
                Map paramMap = (Map) parameterObject;
                //判断key是否有et字符串
                if(paramMap.containsKey("et")){
                    parameterObject=paramMap.get("et");
                }

            }
            parameterObjectClass=parameterObject.getClass();
            //校验该实例的类是否被@SensitiveData所注解
            SensitiveData sensitiveData = AnnotationUtils.findAnnotation(parameterObjectClass, SensitiveData.class);
            if (Objects.nonNull(sensitiveData)) {
                //取出当前当前类所有字段,传入加密方法
                Field[] declaredFields = parameterObjectClass.getDeclaredFields();
                AesUtils.encrypt(declaredFields, parameterObject);
            }
        }
        return invocation.proceed();
    }

    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }

    @Override
    public void setProperties(Properties properties) {

    }

}

  • 6
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

半夜燃烧的香烟

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值