随笔 数据库敏感数据加密存储

场景

应公司安全部门要求,需要对数据库敏感数据进行加密存储(第一期只包含证件号&手机号)。
由于这是一个技改类需求,与业务无关,我们考虑用自定义注解+aop来做(orm用mybatis,aop选用Aspectj)。这样做对业务代码没有侵入,并且后期扩展非常方便。

code

NeedEncryption
@Retention(RetentionPolicy.RUNTIME)
@Target(value = {ElementType.TYPE, ElementType.FIELD})
@Documented
public @interface NeedEncryption {
}
DataEncryptionAspect
import org.apache.commons.lang.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

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

@Aspect
@Component
public class DataEncryptionAspect {
    @Pointcut(value = "execution(* com.tbryant.mapper.*.*(..))")
    public void encryptAndDecrypt() {
    }

    @Around("encryptAndDecrypt()")
    public Object handle(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        // before
        Object[] args = proceedingJoinPoint.getArgs();
        for (Object arg : args) {
            // 集合类型目前只支持List,处理其他类型需要扩展
            if (arg instanceof List) {
                for (Object obj : (List) arg) {
                    doEncrypt(obj, true);
                }
            } else {
                doEncrypt(arg, true);
            }
        }

        // proceed
        Object result;
        result = proceedingJoinPoint.proceed(args);

        // after
        // 集合类型目前只支持List,处理其他类型需要扩展
        if (result instanceof List) {
            for (Object obj : (List) result) {
                doEncrypt(obj, false);
            }
        } else {
            doEncrypt(result, false);
        }
        return result;
    }

    private void doEncrypt(Object obj, Boolean isEncrypt) throws Exception {
        if (null != obj) {
            Class objClazz = obj.getClass();
            if (objClazz.isAnnotationPresent(NeedEncryption.class)) {
                Field[] clazzFields = objClazz.getDeclaredFields();
                for (Field field : clazzFields) {
                    // 需要脱敏的字段类型目前只支持String
                    if ("class java.lang.String".equals(field.getGenericType().toString()) && field.isAnnotationPresent(NeedEncryption.class)) {
                        Method getFieldMethod = objClazz.getMethod("get" + getMethodName(field.getName()));
                        Method setFieldMethod = objClazz.getMethod("set" + getMethodName(field.getName()), String.class);
                        String fieldValue = (String) getFieldMethod.invoke(obj);
                        if (StringUtils.isNotBlank(fieldValue)) {
                            setFieldMethod.invoke(obj, handle(fieldValue, isEncrypt));
                        }
                    }
                }
            }
        }
    }

    private String handle(String content, Boolean isEncrypt) throws Exception {
        if (StringUtils.isBlank(content)) {
            return content;
        }
        // 加解密算法可自行替换,对称非对称都可以
        return isEncrypt ? DESUtils.encrypt(content) : DESUtils.decrypt(content);
    }

    private static String getMethodName(String fildeName) {
        byte[] items = fildeName.getBytes();
        items[0] = (byte) ((char) items[0] - 'a' + 'A');
        return new String(items);
    }
}

使用

由于是非侵入式设计,所以使用起来非常方便。找到需要加密的字段,在该PO和该字段上添加@NeedEncryption即可。
为何PO上也要添加注解:主要是为了提升效率,如果PO上没有@NeedEncryption,那就直接跳过加密处理。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值