使用Mybatis-plus拦截加密数据

2023-05-16

使用Mybatis-plus拦截加密数据

使用自定义注解来标识需要加密的po和字段,并通过mybaitsplus的插件工具类Interceptor类来实现对数据的拦截与加密转换操作。
一、自定义加密注解
作用在类上的注解

package com.richstonedt.cmp.model.annotate;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.TYPE})//该参数代表是作用在类
@Retention(RetentionPolicy.RUNTIME)
public @interface SensitiveBean {
    String value() default "";
}


作用在字段上的注解

package com.richstonedt.cmp.model.annotate;
import java.lang.annotation.*;

@Target({ ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
public @interface EncryptField {
    String value() default "";
}

二、加密拦截
拦截执行为update的操作。首先对包含了SensitiveBean注解的类进行判断,包含则对字字段中包含EncryptField注解的字段数据进行加密操作。(此操作过程通过反射和Field进行操作,有更好的建议可以提出来哦~)
在定义拦截操作时,最重要的是@Intercept中的定义,method决定能拦截到的mybatis的操作方法的类别

package com.richstonedt.cmp.common.interceptor;

import com.richstonedt.cmp.common.util.EncryptUtil;
import com.richstonedt.cmp.model.annotate.EncryptField;
import com.richstonedt.cmp.model.annotate.SensitiveBean;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.binding.MapperMethod;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.executor.parameter.ParameterHandler;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ParameterMap;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Signature;
import org.springframework.util.StringUtils;

import java.lang.reflect.Field;

//update包含insert、update、delete
@Intercepts({
        @Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class})
})
@Slf4j
public class ParammeterInterceptor implements Interceptor {

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        String methodName = invocation.getMethod().getName();
        if (!"update".equalsIgnoreCase(methodName) && !"insert".equalsIgnoreCase(methodName)) {
           return invocation.proceed();
        }
        // 获取该sql语句放入的参数
        Object parameter = invocation.getArgs()[1];
        if (parameter instanceof ParameterMap) {
            MapperMethod.ParamMap paramMap = (MapperMethod.ParamMap) invocation.getArgs()[1];
            parameter = paramMap.get("param1");
        }

        if (parameter instanceof MapperMethod.ParamMap) {
            MapperMethod.ParamMap paramMap = (MapperMethod.ParamMap) invocation.getArgs()[1];
            try {
                parameter = paramMap.get("et");
            } catch (Exception e) {
                parameter = paramMap.get("param1");
            }
        }

        if (parameter != null && isSensitiveBean(parameter)) {
            // 对参数内含注解的字段进行加密
            encryptField(parameter);
        }
        return invocation.proceed();
    }

    /**
     * 判断是否带有敏感的实体类
     *
     * @param t
     * @param <T>
     * @return
     */
    public <T> boolean isSensitiveBean(T t) {
        Class object = null;
        try {
            object = (Class) t;
        } catch (Exception e) {
            object = t.getClass();
        }
        if (object != null && object.isAnnotationPresent(SensitiveBean.class)) {
            return true;
        }
        return false;
    }

    /**
     * 加密类中的敏感字段
     *
     * @param t
     * @param <T>
     */
    private <T> void encryptField(T t) {
        Field[] declaredFields = t.getClass().getDeclaredFields();
        if (declaredFields != null && declaredFields.length > 0) {
            for (Field field : declaredFields) {
                // 查找当字段带有加密注解,并且字段类型为字符串类型
                if (field.isAnnotationPresent(EncryptField.class)) {
                    field.setAccessible(true);
                    String fieldValue = null;
                    try {
                        fieldValue = (String) field.get(t);
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    }
                    if (!StringUtils.isEmpty(fieldValue)) {
                        try {
                            field.set(t, EncryptUtil.aesEncrypt(fieldValue));
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
    }
}

三、解密拦截
同加密拦截相同,通过SensitiveBean和EncryptField进行判断是否需要解密,并通过进一步字符特征判断是否是加密过的数据,再考虑解密。

package com.richstonedt.cmp.common.interceptor;

import com.richstonedt.cmp.common.util.EncryptUtil;
import com.richstonedt.cmp.model.annotate.EncryptField;
import com.richstonedt.cmp.model.annotate.SensitiveBean;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;

@Intercepts({
        @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}),
})
@Slf4j
public class ResultInterceptor implements Interceptor {
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        Object returnValue = invocation.proceed();
        try {
            // 当返回值类型为数组集合时,就判断是否需要进行数据解密
            if (returnValue instanceof ArrayList<?>) {
                List<?> list = (List<?>) returnValue;
                if (CollectionUtils.isEmpty(list)) {
                    return returnValue;
                }
                Object object = list.get(0);
                // 判断第一个对象是否有解密注解
                if (object != null && isSensitiveBean(object)) {
                    for (Object o : list) {
                        decryptField(o);
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
            return returnValue;
        }
        return returnValue;
    }

    /**
     * 解密
     *
     * @param t
     * @param <T>
     */
    public <T> void decryptField(T t) {
        Field[] declaredFields = t.getClass().getDeclaredFields();
        if (declaredFields != null && declaredFields.length > 0) {
            // 遍历这些字段
            for (Field field : declaredFields) {
                // 如果这个字段存在解密注解就进行解密
                if (field.isAnnotationPresent(EncryptField.class) && field.getType().toString().endsWith("String")) {
                    field.setAccessible(true);
                    try {
                        // 获取这个字段的值
                        String fieldValue = (String) field.get(t);
                        // 判断这个字段的数值是否不为空
                        if (!StringUtils.isEmpty(fieldValue)) {
                            if (EncryptUtil.isEncrypted(fieldValue)){
                                // 进行解密
                                String encryptData = EncryptUtil.aesDecrypt(fieldValue);
                                // 将值反射到对象中
                                field.set(t, encryptData);
                            }else {
                                log.info("该数据未被加密");
                            }
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }

    /**
     * 判断是否带有敏感的实体类
     *
     * @param t
     * @param <T>
     * @return
     */
    public <T> boolean isSensitiveBean(T t) {
        Class object=null;
        try {
            object=(Class) t;
        }catch (Exception e){
            object=t.getClass();
        }
        if (object!=null&&object.isAnnotationPresent(SensitiveBean.class)) {
            return true;
        }
        return false;
    }
}

四、加密方法
EncryptUtil的加密解密方法可自行定义。aesEncrypt为加密,aesDecrypt为解密。因为本人当时的项目加密要求是后面提的,因多种原因使用了AES加密,偏移量模式选择了ECB(默认为CBC),选择ECB是因为postgresql数据库中的加密方法为aes的ecb

package com.richstonedt.cmp.common.util;

import org.apache.commons.codec.binary.Base64;
import org.springframework.util.StringUtils;

import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.math.BigDecimal;
import java.security.Key;
import java.util.regex.Pattern;

public class EncryptUtil {
    private static final String KEY = "f4k9f5w7f8g4er26";  // 密匙,必须16位
    private static final String OFFSET = "5e8y6w45ju8w9jq8"; // 偏移量
    private static final String ENCODING = "UTF-8"; // 编码
    private static final String ALGORITHM = "AES"; //算法
    private static final String CIPHER_ALGORITHM = "AES/ECB/PKCS5Padding"; // 默认的加密算法,CBC模式
    private static final String BASE64_REGX= "^([A-Za-z0-9+/]{4})*([A-Za-z0-9+/]{4}|[A-Za-z0-9+/]{3}=|[A-Za-z0-9+/]{2}==)$"; //验证

    /**
     * 加密
     * @param data
     * @return
     * @throws Exception
     */
    public static String aesEncrypt(String data) throws Exception {
        Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
        SecretKeySpec skeySpec = new SecretKeySpec(KEY.getBytes("ASCII"), ALGORITHM);
        //IvParameterSpec iv = new IvParameterSpec(OFFSET.getBytes());//CBC模式偏移量IV
        cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
        byte[] encrypted = cipher.doFinal(data.getBytes(ENCODING));
        return new Base64().encodeToString(encrypted);//加密后再使用BASE64做转码
    }

    /**
     * 解密
     * @param data
     * @return
     * @throws Exception
     */
    public static String aesDecrypt(String data) throws Exception {
        data.replace(" ","+");
        Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
        SecretKeySpec skeySpec = new SecretKeySpec(KEY.getBytes("ASCII"), ALGORITHM);
        //IvParameterSpec iv = new IvParameterSpec(OFFSET.getBytes()); //CBC模式偏移量IV
        cipher.init(Cipher.DECRYPT_MODE, skeySpec);
        byte[] buffer = new Base64().decode(data);//先用base64解码
        byte[] encrypted=encrypted = cipher.doFinal(buffer);
        return new String(encrypted, ENCODING);
    }


    /**
     * 验证是否加密
     * @param data
     * @return
     */
    public static Boolean isEncrypted(String data){
        if (Pattern.matches(BASE64_REGX,data)&&data.endsWith("==")){
            return true;
        }
        return false;
    }
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

使用Mybatis-plus拦截加密数据 的相关文章

  • 栈的基础知识

    0 简介 最近在自己编写一些小的算法的时候 xff0c 深感自己的算法过于臃肿 碰巧Datawhale在新的一期组队学习中组织了数据结构与算法的课程学习 于是就参加了 xff0c 再次感谢Datawhale 首先跟大家分享一下两个自己感觉比

随机推荐

  • AD BOM表导出

    AD BOM表导出 在REPOERT下选择bill of materials 即进行BOM输出 输出操作网上有许多教程 xff0c 这里不进行叙述 xff0c 只提一下 xff0c 必须将Value选择打钩并上拉到展示纵队中 制作自己的模板
  • STM32 的OLED的使用

    7脚OLED依次有引脚 xff1a GND VCC D0 D1 RES DC CS七个脚 模块接口定义 xff1a 1 GND 电源地 2 VCC 电源正 xff08 3 xff5e 5 5V xff09 3 D0 OLED 的 D0 脚
  • 【转载】关于Visual Studio、VC和C++的那些事

    size 61 medium 首先 xff0c 这三个东西有什么区别呢 xff1f VC和C 43 43 是相同的吗 xff0c 有什么区别呢 xff1f 我刚开始学C 43 43 的时候也有这样的问题 xff0c 在这里我来替大家解释一下
  • Byte、bit 、和16进制之间的关系

    bit xff08 比特 xff09 byte xff08 字节 xff09 一 bit xff08 比特 xff09 计算机内存储和处理信息的最小单位是位 xff08 bit 或比特 xff09 xff0c 一个比特值可以是0或1 xff
  • ROS常见问题1——找不到包

    ROS常见问题1 找不到包 1 问题 每次打开终端会提示找不到相关包或者相关文件 比如在查看自己在catkin工作区间下创建的消息时提示找不到文件 xff1a 输入 xff1a span class hljs variable span r
  • 架空线路的基本结构及组成

    最近没有什么实时的新闻发 xff0c 所以小编就给大家整理了一些干货 准备好了吗 xff1f 具体是什么干货呢 xff1f 这句话问的好 xff0c 今天小编就给大家聊一聊电力巡检的基本知识 xff1a 架空线路的基本构成及组成 架空输电线
  • 微信小程序云开发,数据库数据的增加

    1 在wxml中添加一个按钮绑定事件 xff0c 添加数据 2 在云数据库中新建一个数据文件夹list 3 在JS中添加数据 4 在数据库中即可有增加的数据
  • Anaconda如何成功配置OpenCV的开发环境

    1 打开Anaconda的运行环境 xff1a 点击Anaconda Prompt运行 2 进入环境 使盘符在Anaconda 安装目录下 进入Scripts文件夹下 xff1b 3 找到相应路径位置 xff1a 4 敲代码运行到相应盘符
  • jupyter notebook快速入门使用详解及标记的使用(Markdown使用笔记)

    1 软件 xff08 终端的打开 xff09 打开软件之后 xff0c Jupyter Notebook 将在你的默认浏览器中打开 xff0c 网址为 xff1a http localhost 8888 tree 在某些情况下 xff0c
  • F3飞控的调试

    http www moz8 com thread 109506 1 1 html
  • 航模飞机设计基础知识

    部分引用源1 xff1a KSP飞机设计简易指南 http tieba baidu com p 2272016546 FAR进阶气动稳定性和控制教程 http bbs deeptimes org forum php mod 61 viewt
  • SLA技术3D打印机的原理

    SLA是Stereo lithography Appearance的缩写 xff0c 即立体光固化成型法 用特定波长与强度的激光聚焦到光固化材料表面 xff0c 使之由点到线 xff0c 由线到面顺序凝固 xff0c 完成一个层面的绘图作业
  • STM32F103C8T6读取气压计MS5611,I2C读取模式

    笔者最近想用气压计模块来测一下相对高度 xff0c 使用的元器件如下图所示 所使用的最小系统板 所使用的气压计模块 其实读取还是蛮简单的 xff0c 根据核心板引脚图选择I2c接口 xff0c 然后借鉴正点原子的模拟i2c程序 xff0c
  • keil 的头文件

    许多初学者使用网上下载的程序时都会遇到这样一个问题 xff0c 就是头文件找不到 我想就这个问题说明一下 首先 xff0c 我们用到的KEIL有几种版本的 xff0c 头文件也不同 有reg51 h和at89x51 h两种比较常见 at89
  • 关于串口的初始化Uart_Init(0, 115200)

    void Uart Init int pclk int baud int i if pclk 61 61 0 因为Main c 中定义了 GLOBAL CLK 61 1 所以 PCLK 在 option h 中定义 在Main c 中的设置
  • 【学习笔记】Ubuntu双系统+搭建个人服务器

    Ubuntu双系统 43 搭建个人服务器 前言1 Ubuntu 43 Win双系统1 1 制作U盘启动盘1 2 系统分盘1 3 安装Ubuntu系统 2 搭建个人服务器2 1 设置root2 2 配置ssh2 3 向日葵连接 3 内网穿透3
  • IMU 测量模型和运动学模型

    一 概念 高斯白噪声 测量噪声是AD转换器件引起的外部噪声 xff0c 波动激烈的测量白噪声 随机游走 这里指零偏Bias 随机游走噪声 xff0c 是传感器内部机械 温度等各种物理因素产生的传感器内部误差的综合参数 xff0c 是变化缓慢
  • java参数校验注解

    java参数校验注解 java中前后台参数传递时如何对参数进行校验 校验主要使用到 javax validation类 一 引入依赖 SpringBoot的web组件中已引入validation的jar包 xff0c 但也可自行引入 spa
  • SpringBoot集成阿里easyexcel(三)CellWriteHandler图片转换

    继承单元格处理器 xff0c 通过重写不同方法 xff0c 对单元格进行处理 span class token keyword public span span class token keyword class span span cla
  • 使用Mybatis-plus拦截加密数据

    使用Mybatis plus拦截加密数据 使用自定义注解来标识需要加密的po和字段 xff0c 并通过mybaitsplus的插件工具类Interceptor类来实现对数据的拦截与加密转换操作 一 自定义加密注解 作用在类上的注解 pack