用aop对敏感数据加解密

某会期间监控严格,突然要求对数据库里一些数据做加密。

斜体样式参考的是这篇敏感数据加解密aop实现

原理就是在需要加解密的数据库对象跟字段上做注解,用aop拦截mapper类,然后过滤接口参数,有对应的注解就做加密和解密。

然后遇到一个问题,就是加密之后其实修改对象的值,这个值如果在插入数据库之后还有需要用到的地方就比较麻烦。所以这里加了一段对象拷贝的内容。也是网上找的代码:JAVA实现对象的深复制

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.List;

/**
 * @Type CryptPojoUtils.java
 * @Desc
 * @Author chentuoyi
 * @Date 2020/5/25 10:03
 * @Version 1.0
 */
@Slf4j
public class CryptPojoUtils {

    private final static String KEY = "xxxxxx";
    private final static String AES_IV = "xxxxxxx";


    /**
     * 对含注解字段解密
     */
    public static <T> void decryptFieldOrList(T t) {
        if (null == t) {
            return;
        }
        if (t instanceof List) {
            List values = (List) t;
            for (Object object : values) {
                CryptPojoUtils.decryptField(object);
            }
        } else {
            CryptPojoUtils.decryptField(t);
        }
    }

    /**
     * 对含注解字段解密
     */
    private static <T> void decryptField(T t) {
        SecureClass annotation = t.getClass().getAnnotation(SecureClass.class);
        if (null == annotation) {
            return;
        }
        log.info("deal with decrypt field in {}", t.getClass());
        Field[] declaredFields = t.getClass().getDeclaredFields();
        try {
            if (declaredFields != null && declaredFields.length > 0) {
                for (Field field : declaredFields) {
                    if (field.isAnnotationPresent(SecureParam.class)
                            && field.getType().toString().endsWith("String")) {
                        log.info("deal with decrypt field at {}", field.getName());
                        field.setAccessible(true);
                        String fieldValue = (String) field.get(t);
                        //这里主要对原数据长度做了比较,兼容数据库里没加密的原始数据
                        if (StringUtils.isNotEmpty(fieldValue) && fieldValue.length() >= 32) {
                            field.set(t, AESUtil.decrypt(KEY, AES_IV, fieldValue));
                        }
                    }
                }
            }
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 对含注解字段加密
     */
    private static <T> T encryptField(T param) {
        if (null == param) {
            return param;
        }
        SecureClass annotation = param.getClass().getAnnotation(SecureClass.class);


        if (null == annotation) {
            return param;
        }
        log.info("deal with encrypt field in {}", param.getClass());


        try {
            T paramCopy = deepcopy(param);
            Field[] declaredFields = paramCopy.getClass().getDeclaredFields();
            if (declaredFields != null && declaredFields.length > 0) {
                for (Field field : declaredFields) {
                    if (field.getAnnotation(SecureParam.class) != null
                            && field.getType().toString().endsWith("String")) {
                        log.info("deal with encrypt field at {}", field.getName());
                        field.setAccessible(true);
                        String fieldValue = (String) field.get(paramCopy);
                        if (StringUtils.isNotEmpty(fieldValue)) {
                            field.set(paramCopy, AESUtil.encrypt(KEY, AES_IV, fieldValue));
                        }
                    }
                }
            }
            return paramCopy;
        } catch (IOException | ClassNotFoundException e) {
            log.error(e.getMessage(), e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        }
        return param;
    }

    /**
     * 对含注解字段加密
     */
    public static <T> Object[] encryptFields(T[] objects) {
        if (objects.length != 0) {
            Object[] encryptObjects = new Object[objects.length];

            for (int i = 0; i < encryptObjects.length; i++) {
                T encryptObject = encryptField(objects[i]);
                encryptObjects[i] = encryptObject;
            }
            return encryptObjects;
        }
        return objects;
    }

    /**
     * @param object
     *
     * @return
     */
    public static String encryptObject(Object object) {
        if (null == object) {
            return null;
        }
        return AESUtil.encrypt(KEY, AES_IV, String.valueOf(object));
    }
    //深拷贝对象
    private static <T> T deepcopy(T src) throws IOException, ClassNotFoundException {

        ByteArrayOutputStream byteout = new ByteArrayOutputStream();

        ObjectOutputStream out = new ObjectOutputStream(byteout);

        out.writeObject(src);

        ByteArrayInputStream bytein = new ByteArrayInputStream(
                byteout.toByteArray());

        ObjectInputStream in = new ObjectInputStream(bytein);

        return (T) in.readObject();
    }


}

今天突然灵机一动,想到mybatis-plus有没有现成的轮子可用。一搜还真有mybatis plus 实现敏感数据的加密

但是在代码里还不能用,统一的mybatis plus版本还在3.1 。也不算白写。

那就参考下这个的实现吧~
——————————————6月4日更新分割线———————————————
之前一直有问题,报类似这样的错误:
java.lang.NoSuchMethodError: org.springframework.util.ClassUtils.isPresent(Ljava/lang/String;Ljava/l
虽然知道是版本不一致引起的,但是要定位哪个版本不对还是挺烦的。
首先搞的是项目里的一个模块,外面还有parent的pom文件,依赖比较复杂。
最近升级了一下idea,查maven依赖更加方便了,才让我想再看下。

最后发现是org.mybatis的版本不对要升级,换成3.5.4就能用3.3.1的mybatis-plus了。get√
还有不要实现StringTypeHandler 会默认加载你实现的那个,那就对每个String类型都做加密了

ps:升级idea也遇到很多问题

——————————————————————————————

新问题

不知道是不是mockito跟mybatis有啥冲突,用mockito方式做单元测试会报这个错
com.baomidou.mybatisplus.core.exceptions.MybatisPlusException: can not find lambda cache for this entity

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值