springboot+mybatis+自定义注解实现数据脱敏

废话不多说,直接上源码

1.脱敏自定义注解类

该注解可以用在方法参数上,也可以用在对象的属性上,使用该注解就标识参数或属性需要脱敏

import java.lang.annotation.*;

@Documented
@Inherited
@Target({ElementType.FIELD,ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface Desensitization {

}

2.切方法自定义注解

该注解用于方法上,标识该方法需执行数据库出入库的脱敏操作

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.*;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented
public @interface EncryptTransaction {
}

 3.加解密工具类

使用了AES加密方法对数据库出入库进行加解密

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Base64;

public class AESUtils {

    private static final String DEFAULT_V = "********";//用户自定义
    private static final String ALGORITHM = "AES";
    private static final String KEY = "***********";//用户自定义

    private static String byteToHexString(byte[] bytes) {
        StringBuilder sb = new StringBuilder();
        for (byte aByte : bytes) {
            String strHex = Integer.toHexString(aByte);
            if (strHex.length() > 3) {
                sb.append(strHex.substring(6));
            } else {
                if (strHex.length() < 2) {
                    sb.append("0").append(strHex);
                } else {
                    sb.append(strHex);
                }
            }
        }
        return sb.toString();
    }


    private static SecretKeySpec getKey() {
        byte[] arrBTmp = KEY.getBytes();
        // 创建一个空的16位字节数组(默认值为0)
        byte[] arrB = new byte[16];
        for (int i = 0; i < arrBTmp.length && i < arrB.length; i++) {
            arrB[i] = arrBTmp[i];
        }
        return new SecretKeySpec(arrB, ALGORITHM);
    }

    /**
     * 加密
     */
    public static String encrypt(String content) throws Exception {
        final Base64.Encoder encoder = Base64.getEncoder();
        SecretKeySpec keySpec = getKey();
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        IvParameterSpec iv = new IvParameterSpec(DEFAULT_V.getBytes());
        cipher.init(Cipher.ENCRYPT_MODE, keySpec, iv);
        byte[] encrypted = cipher.doFinal(content.getBytes());
        return encoder.encodeToString(encrypted);
    }


    /**
     * 解密
     */
    public static String decrypt(String content) throws Exception {
        final Base64.Decoder decoder = Base64.getDecoder();
        SecretKeySpec keySpec = getKey();
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        IvParameterSpec iv = new IvParameterSpec(DEFAULT_V.getBytes());
        cipher.init(Cipher.DECRYPT_MODE, keySpec, iv);
        byte[] base64 = decoder.decode(content);
        byte[] original = cipher.doFinal(base64);
        return new String(original);
    }

}

4.切面实现类

import com.bt.dlife.common.annotation.Desensitization;
import com.bt.dlife.common.utils.AESUtils;
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.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import javax.crypto.IllegalBlockSizeException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.List;

@Component
@Aspect
public class DesensitizationImpl {

    @Pointcut("@annotation(com.bt.dlife.common.annotation.EncryptTransaction)")
    private void EncryptTransaction(){

    }

    @Around("EncryptTransaction()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        Object[] objects = joinPoint.getArgs();//获取方法参数值
        if(objects!=null){
            for(Object o : objects){
                if(null!=o){
                    if(o instanceof List){//如果是由对象组成的list
                        List objectArrayList = (List) o;//强转成list
                        for(Object object : objectArrayList){
                            encrypt(object);//执行加密方法
                        }
                    }else{
                        encrypt(o);
                    }
                }
            }
            //获取方法参数上的注解
            MethodSignature signature = (MethodSignature) joinPoint.getSignature();
            Method method = signature.getMethod();
            Annotation[][] annotations = method.getParameterAnnotations();
            for (int i = 0; i < annotations.length; i++) {
                Annotation[] paramAnn = annotations[i];
                if(paramAnn.length == 0){
                    continue;
                }
                for (Annotation annotation : paramAnn) {
                    if(annotation instanceof Desensitization){
                        if(objects[i] instanceof String){
                            objects[i] = AESUtils.encrypt(objects[i].toString());
                        }
                        if(objects[i] instanceof List){
                            List value = (List) objects[i];
                            List newValue = value.stream().map(object->{
                                try {
                                    return AESUtils.encrypt(String.valueOf(object));
                                } catch (Exception e) {
                                    e.printStackTrace();
                                }
                                return String.valueOf(object);
                            }).collect(Collectors.toList());
                            objects[i] = newValue;
                        }
                    }
                }
            }
        }
        Object object = joinPoint.proceed(objects);//将修改后的参数传到方法中获取执行结果
        if(object!=null){
            if(object instanceof List){//接收结果是list
                List objectArrayList = (List) object;
                for(int i = 0;i< objectArrayList.size();i++){
                    if(objectArrayList.get(i) instanceof String){
                        String res = (String) objectArrayList.get(i);
                        try{
                            res = AESUtils.decrypt(res);
                            objectArrayList.set(i,res);
                        }catch (IllegalBlockSizeException | IllegalArgumentException e){
                            objectArrayList.set(i,res);//这里防止接收结果不需要解密所以不活了相应异常
                        }
                    }else{//list里面是自定义对象
                        decrypt(objectArrayList.get(i));
                    }

                }
            }else if(object instanceof String){//接收结果是String
                try{
                    object = AESUtils.decrypt(object.toString());
                }catch (IllegalBlockSizeException | IllegalArgumentException e){
                    return object;
                }
            }else{//接收结果是自定义对象
                decrypt(object);
            }
        }
        return object;
    }

    private void encrypt(Object o) throws Exception {
        Field[] fields = o.getClass().getDeclaredFields();
        for(Field field : fields){
            Annotation[] annotations = field.getAnnotations();
            for(Annotation annotation : annotations){
                if(annotation instanceof Desensitization){
                    field.setAccessible(true);
                    if(field.get(o)!=null){
                        Object value = field.get(o);
                        if(value instanceof List){
                            List objectList = (List) value;
                            List list = objectList.stream().map(object-> {
                                try {
                                    return AESUtils.encrypt(String.valueOf(object));
                                } catch (Exception e) {
                                    e.printStackTrace();
                                }
                                return String.valueOf(object);
                            }).collect(Collectors.toList());
                            field.set(o,list);
                            return;
                        }
                        if(value instanceof String){
                            field.set(o, AESUtils.encrypt(String.valueOf(field.get(o))));
                        }
                    }
                }
            }
        }
    }

    private void decrypt(Object o) throws Exception {
        Field[] fields = o.getClass().getDeclaredFields();
        for(Field field : fields){
            Annotation[] annotations = field.getAnnotations();
            for(Annotation annotation : annotations){
                if(annotation instanceof Desensitization){
                    field.setAccessible(true);
                    if(field.get(o)!=null){
                        try{
                            field.set(o, AESUtils.decrypt(String.valueOf(field.get(o).toString())));
                        }catch (IllegalBlockSizeException | IllegalArgumentException e){
                            field.set(o,field.get(o));
                        }
                    }
                }
            }
        }
    }
} 
  

5.使用示例

@EncryptTransaction
    List selectUserId(String name, @Desensitization String phone,String jiraName,Integer team);
@Data
public class UserData {

    private String name;

    @Desensitization
    private String userId;

    @Desensitization
    private String phone;

    private Integer team;

    private String jiraName;

}

6.注意事项

有的小伙伴可能会问mybatis自带了一些方法这时候加解密好不好使呢?

这时候应该是不好使的,但也提供了一个解决办法,就是在@Pointcut这里修改一下切入点,例如

@Pointcut("execution(* com.*.*.*.mapper.*.*(..))||@annotation(com.bt.dlife.common.annotation.EncryptTransaction)")

这种情况可以切到mybatis的自带的一下方法,只有传入实体类,并在实体类的属性上使用@Desensitization注解才可以进行加密。

这样就可以实现脱敏了

你可能感兴趣的:(spring,boot,mybatis,java,后端)