mybatis-plus实现数据字段加解密

目录

1、对称加解密工具类

2、拦截器

2.1、解密拦截器

2.2、加密拦截器

3、注解

3.1、EncryptedField

3.2、EncryptedTable

4、使用实例


1、对称加解密工具类

package com.demo.modules.desensitization;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.crypto.SecureUtil;
import com.demo.modules.desensitization.annotation.EncryptedField;
import com.demo.modules.desensitization.annotation.EncryptedTable;
import org.springframework.core.annotation.AnnotationUtils;

import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.util.Objects;
/**
 * @title 对称加解密工具类
 * @description 主要用于mybatis拦截器中加解密字段
 * @author: Wanglei
 * @create 2024/4/12 0012 18:39:40
 */
public class EncryptDecryptUtils {

    private static final byte[] KEYS = "Apa987^%$321c2mp".getBytes(StandardCharsets.UTF_8);
    /**
     * 对指定的对象进行加密处理。
     * 该方法首先获取对象的类信息,然后检查该类是否被@EncryptedTable注解标记。如果被标记,
     * 则遍历该类的所有字段,对这些需要加密的字段进行加密处理。
     *
     * @param parameterObject 需要进行加密处理的对象。该对象的类必须被@EncryptedTable注解标记。
     * @throws IllegalAccessException 如果在访问字段时发生访问权限问题,则抛出此异常。
     */
    public static void doEncrypt(Object parameterObject) throws IllegalAccessException{
        if(ObjectUtil.isEmpty(parameterObject)){
            return;
        }
        // 获取传入对象的类信息
        Class<?> parameterObjectClass = parameterObject.getClass();
        // 尝试查找该类上的@EncryptedTable注解
        EncryptedTable encryptDecryptClass = AnnotationUtils.findAnnotation(parameterObjectClass, EncryptedTable.class);
        if (Objects.nonNull(encryptDecryptClass)){
            // 获取类中所有声明的字段
            Field[] declaredFields = parameterObjectClass.getDeclaredFields();
            // 对所有字段进行加密处理
            encrypt(declaredFields, parameterObject);
        }
    }
    /**
     * 多field加密方法
     *
     * @param declaredFields
     * @param parameterObject
     * @param <T>
     * @return
     * @throws IllegalAccessException
     */
    public static <T> T encrypt(Field[] declaredFields, T parameterObject) throws IllegalAccessException {
        for (Field field : declaredFields) {
            EncryptedField annotation = field.getAnnotation(EncryptedField.class);
            if (Objects.isNull(annotation)) {
                continue;
            }
            encrypt(field, parameterObject);
        }
        return parameterObject;
    }


    /**
     * 单个field加密方法
     *
     * @param field
     * @param parameterObject
     * @param <T>
     * @return
     * @throws IllegalAccessException
     */
    public static <T> T encrypt(Field field, T parameterObject) throws IllegalAccessException {
        field.setAccessible(true);
        Object object = field.get(parameterObject);
        if (object instanceof BigDecimal) {
            BigDecimal value = (BigDecimal) object;
            long longValue = value.movePointRight(4).subtract(BigDecimal.valueOf(Integer.MAX_VALUE >> 3)).longValue();
            field.set(parameterObject, BigDecimal.valueOf(longValue));
        } else if (object instanceof Integer) {

        } else if (object instanceof Long) {

        } else if (object instanceof String) {
            //定制String类型的加密算法
            String value = (String) object;
            field.set(parameterObject, SecureUtil.aes(KEYS).encryptHex(value));
        }
        return parameterObject;
    }

    /**
     * 解密方法
     *
     * @param result
     * @param <T>
     * @return
     * @throws IllegalAccessException
     */
    public static <T> T decrypt(T result) throws IllegalAccessException {
        Class<?> parameterObjectClass = result.getClass();
        Field[] declaredFields = parameterObjectClass.getDeclaredFields();
        decrypt(declaredFields, result);
        return result;
    }

    /**
     * 多个field解密方法
     *
     * @param declaredFields
     * @param result
     * @throws IllegalAccessException
     */
    public static void decrypt(Field[] declaredFields, Object result) throws IllegalAccessException {
        for (Field field : declaredFields) {
            EncryptedField annotation = field.getAnnotation(EncryptedField.class);
            if (Objects.isNull(annotation)) {
                continue;
            }
            decrypt(field, result);
        }
    }

    /**
     * 单个field解密方法
     *
     * @param field
     * @param result
     * @throws IllegalAccessException
     */
    public static void decrypt(Field field, Object result) throws IllegalAccessException {
        field.setAccessible(true);
        Object object = field.get(result);
        if (object instanceof BigDecimal) {
            BigDecimal value = (BigDecimal) object;
            double doubleValue = value.add(BigDecimal.valueOf(Integer.MAX_VALUE >> 3)).movePointLeft(4).doubleValue();
            field.set(result, BigDecimal.valueOf(doubleValue));
        } else if (object instanceof Integer) {

        } else if (object instanceof Long) {

        } else if (object instanceof String) {
            //定制String类型的解密算法
            String value = (String) object;
            //对注解的字段进行逐一解密
            String decryptStr = value;
            // 使用try-catch捕获异常,处理数据库中未加密的数据,直接返回未加密数据
            try{
                decryptStr = SecureUtil.aes(KEYS).decryptStr(value);
            }catch (Exception e){
                e.printStackTrace();
            }
            field.set(result, decryptStr);
        }
    }

}

2、拦截器

2.1、解密拦截器

package com.demo.modules.desensitization.interceptor;

import com.demo.modules.config.Properties;
import com.demo.modules.desensitization.EncryptDecryptUtils;
import com.demo.modules.desensitization.annotation.EncryptedTable;
import org.apache.ibatis.executor.resultset.ResultSetHandler;
import org.apache.ibatis.plugin.*;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Objects;

/**
 * @title 解密拦截器
 * @description 解密拦截器
 * @author: Wanglei
 * @create 2024/4/12 0012 17:40:02
 */
@Intercepts({
        @Signature(type = ResultSetHandler.class, method = "handleResultSets", args = {Statement.class})
})
@Component
public class DecryptInterceptor implements Interceptor {

    @Resource
    private Properties properties;
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        Object resultObject = invocation.proceed();
        if (Objects.isNull(resultObject)) {
            return null;
        }
        // 未开启脱敏直接返回结果
        if(!properties.getDesensitizationEnabled()){
            return resultObject;
        }
        if (resultObject instanceof ArrayList) {
            //基于selectList
            ArrayList resultList = (ArrayList) resultObject;
            if (!resultList.isEmpty() && needToDecrypt(resultList.get(0))) {
                for (Object result : resultList) {
                    //逐一解密
                    EncryptDecryptUtils.decrypt(result);
                }
            }
        } else if (needToDecrypt(resultObject)) {
            //基于selectOne
            EncryptDecryptUtils.decrypt(resultObject);
        }
        return resultObject;
    }

    /**
     * 校验该实例的类是否被@EncryptedTable所注解
     */
    private boolean needToDecrypt(Object object) {
        Class<?> objectClass = object.getClass();
        EncryptedTable sensitiveData = AnnotationUtils.findAnnotation(objectClass, EncryptedTable.class);
        return Objects.nonNull(sensitiveData);
    }

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

2.2、加密拦截器

package com.demo.modules.desensitization.interceptor;

import com.demo.modules.config.Properties;
import com.demo.modules.desensitization.EncryptDecryptUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.binding.MapperMethod;
import org.apache.ibatis.executor.parameter.ParameterHandler;
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.stereotype.Component;

import javax.annotation.Resource;
import java.lang.reflect.Method;
import java.nio.charset.StandardCharsets;
import java.sql.PreparedStatement;
import java.util.*;

/**
 * @title 加密拦截器
 * @description 加密拦截器
 * @author: Wanglei
 * @create 2024/4/12 0012 16:23:42
 */
@Intercepts({
        @Signature(type = ParameterHandler.class,method = "setParameters",args = PreparedStatement.class)
})
@Slf4j
@Component
public class EncryptInterceptor implements Interceptor {

    @Resource
    private Properties properties;

    /** 获取参数对象方法名称*/
    private final static String getParameterObjectMethod = "getParameterObject";

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        //拦截 ParameterHandler 的 setParameters 方法 动态设置参数
        if (invocation.getTarget() instanceof ParameterHandler && properties.getDesensitizationEnabled()) {
            ParameterHandler parameterHandler = (ParameterHandler) invocation.getTarget();

            // 反射获取 参数方法
            Class<? extends ParameterHandler> aClass = parameterHandler.getClass();
            Method getParameterObject = aClass.getDeclaredMethod(getParameterObjectMethod);
            Object parameterObject = getParameterObject.invoke(parameterHandler);

            if (Objects.nonNull(parameterObject)){
                //mybatis-plus 框架默认多参数
                if (parameterObject instanceof MapperMethod.ParamMap){
                    MapperMethod.ParamMap paramMap = (MapperMethod.ParamMap) parameterObject;
                    //避免相同的key重复执行,使用set对象记录堆栈地址
                    HashSet<Integer> stackSet = new HashSet<>();
                    for (Object key : paramMap.keySet()) {
                        //需要使用hashcode判断,equals()方法可能被重写。
                        if (stackSet.add(System.identityHashCode(paramMap.get(key)))) {
                            EncryptDecryptUtils.doEncrypt(paramMap.get(key));
                        }
                    }
                } else {
                    EncryptDecryptUtils.doEncrypt(parameterObject);
                }
            }
        }
        return invocation.proceed();
    }
}

3、注解

3.1、EncryptedField

package com.demo.modules.desensitization.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * @title 需要加解密的字段用这个注解
 * @description TODO description class purposex
 * @author: Wanglei
 * @create 2024/4/12 0012 16:22:30
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface EncryptedField {
}

3.2、EncryptedTable

package com.demo.modules.desensitization.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * @title 需要加解密的实体类用这个注解
 * @description TODO description class purposex
 * @author: Wanglei
 * @create 2024/4/12 0012 16:22:10
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface EncryptedTable {
}

4、使用方法

在需要加解密的实体类头部添加注解@EncryptedTable,同时在需要加解密的字段上添加@EncryptedField注解即可。

  • 13
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 6
    评论
### 回答1: 可以使用Mybatis-plus提供的update方法来修改本身字段。例如: ```java User user = new User(); user.setId(1L); user.setName("new name"); user.setAge(20); userMapper.updateById(user); ``` 这样就可以将id为1的用户的name和age字段更新为"new name"和20。 ### 回答2: 在使用MyBatis-Plus修改本身字段时,可以通过以下步骤进行操作: 首先,在数据库表中定义需要修改的字段。假设我们有一个名为user的表,其中有一个字段为name。 其次,通过MyBatis-Plus的实体类,例如User实体类,在对应的字段上添加@TableField注解。这样可以告诉MyBatis-Plus字段需要被修改。 然后,在进行数据修改时,通过MyBatis-Plus的update方法来更新表中的数据。可以使用Wrapper类来构建修改条件,然后使用set方法来设置修改的字段值。 最后,调用update方法执行修改操作。 下面是一个示例代码: ```java // 导入需要的类 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; import com.baomidou.mybatisplus.extension.service.additional.update.impl.UpdateChainWrapper; import com.example.demo.entity.User; import com.example.demo.mapper.UserMapper; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @Service public class UserService { @Autowired private UserMapper userMapper; public void updateUser(String id, String name) { // 创建修改条件 UpdateWrapper<User> wrapper = new UpdateWrapper<>(); wrapper.eq("id", id); // 设置修改字段值 User user = new User(); user.setName(name); // 执行更新操作 userMapper.update(user, wrapper); } } ``` 在上述代码中,首先创建了一个UpdateWrapper对象,并指定修改条件为id等于指定的id。然后创建一个User对象,并设置需要修改的字段值。最后调用update方法执行更新操作。 通过以上步骤,就可以实现使用MyBatis-Plus修改本身字段的功能。 ### 回答3: Mybatis-plus是一款基于Mybatis的增强工具,在实现数据库操作时提供了很多便捷的功能。要修改本身字段,可以通过以下步骤实现: 1. 创建一个实体类,映射到数据库的表中。实体类的属性要与表的字段一一对应,并提供对应的getter和setter方法。 2. 在配置文件中配置数据源和Mybatis-plus的相关配置。 3. 编写对应的Mapper接口,通过继承Mybatis-plus中的BaseMapper接口或者自定义接口,定义需要执行的数据库操作方法。 4. 在Mapper接口中定义一个修改数据的方法,并使用Mybatis-plus提供的注解@Update来完成字段的修改。该注解中的value属性可以编写更新语句,使用#{属性名}来引用实体类中的属性。 5. 在Service层中调用Mapper接口中定义的修改方法,将需要修改的数据作为参数传入。 6. 在控制层中调用Service层中的方法,完成数据修改操作。 通过以上步骤,可以使用Mybatis-plus修改本身字段。在修改时需要注意保证实体类中的属性与数据库表的字段一一对应,以及正确配置相关的Mapper接口和注解。
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

燕山石头

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

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

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

打赏作者

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

抵扣说明:

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

余额充值