java annotation desensitization

Brief introduction

With the popularity of the Internet era, user information is becoming more and more important. We also need to desensitize and encrypt user information in the process of developing software. For more complicated tasks, personally explain how to achieve annotation desensitization. Support static call and aop unified interception to realize desensitization or encrypted return.

Code explanation

Desensitization enumeration class

    Define enumeration class, handle all desensitization and encryption, etc., while being extensible, here is just annotated calling method, so as to write samples. DesensitizationEnum If you need other desensitization or encryption methods, you only need to add the following enumeration types

package com.lgh.common.sensitive;


import com.lgh.common.utils.MaskUtils;

import java.lang.reflect.Method;

/**
 * 若需要新定义一个扫描规则,这里添加即可
 *
 * @author lgh
 * @version 1.0
 * @date 2021/1/17
 */
public enum DesensitizationEnum {
    
    

    // 执行类和脱敏方法名
    PHONE(MaskUtils.class, "maskPhone", new Class[]{
    
    String.class});

     private Class<?> clazz;
     private Method method;

    DesensitizationEnum(Class<?> target, String method, Class[] paramTypes) {
    
    
        this.clazz = target;
        try {
    
    
            this.method = target.getDeclaredMethod(method, paramTypes);
        } catch (NoSuchMethodException e) {
    
    
             e.printStackTrace();
        }
    }
    public Method getMethod() {
    
    
        return method;
    }
}

Desensitization tool

package com.lgh.common.utils;

import org.springframework.util.StringUtils;

/**
 * @author lgh
 * @version 1.0
 * @date 2021/1/17
 */
public class MaskUtils {
    
    

    public static String maskPhone(String phone){
    
    
        if(StringUtils.isEmpty(phone) || phone.length() < 8){
    
    
            return phone;
        }
        return phone.replaceAll("(\\d{3})\\d*(\\d{4})", "$1****$2");
    }
}

Annotation class writing

This class can be added to the class attribute that needs to be desensitized to achieve desensitization. Specifically, the annotation is traversed recursively, and the desensitization function is realized through the reflection mechanism.

package com.lgh.common.sensitive;

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

/**
 * 参数定义注解类
 * @author linguohu
 * @version 1.0
 * @date 2021/1/17
 **/
@Target({
    
    ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SensitiveValid {
    
    
    DesensitizationEnum type();
}

Desensitization tools

Special statement, when we recurse, it is index recursion, and there will be an infinite loop. For example, an object refers to an object and a circular address is referenced, so there will be an infinite loop. There are 10 levels of recursion. Generally, we do not allow such deep objects. Set up.

package com.lgh.common.utils;

import com.lgh.common.sensitive.DesensitizationEnum;
import com.lgh.common.sensitive.SensitiveValid;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.ReflectionUtils;

import java.lang.reflect.Field;
import java.util.Collection;
import java.util.Map;

/**
 * 对象脱敏工具
 *
 * @author lgh
 * @version 1.0
 * @date 2021/1/17
 */
public class DesensitizationUtils {
    
    
    private static final Logger log = LoggerFactory.getLogger(DesensitizationUtils.class);

    private DesensitizationUtils() {
    
    
    }

    /**
     * 扫描对象注解,脱敏,最高层次8层
     *
     * @param obj
     */
    public static void format(Object obj) {
    
    
        DesensitizationUtils.formatMethod(obj, 10);
    }

    /**
     * 递归遍历数据,因为可能有对象地址应用导致循环问题,同时设置莫名奇妙的异常,所以设置递归层次,一般不要超过10层
     *
     * @param obj   需要反射对象
     * @param level 递归层次,必须输入
     */
    private static void formatMethod(Object obj, int level) {
    
    
        if (obj == null || isPrimitive(obj.getClass()) || level <= 0) {
    
    
            return;
        }
        if (obj.getClass().isArray()) {
    
    
            for (Object object : (Object[]) obj) {
    
    
                formatMethod(object, level--);
            }
        } else if (Collection.class.isAssignableFrom(obj.getClass())) {
    
    
            for (Object o : ((Collection) obj)) {
    
    
                formatMethod(o, level--);
            }
        } else if (Map.class.isAssignableFrom(obj.getClass())) {
    
    
            for (Object o : ((Map) obj).values()) {
    
    
                formatMethod(o, level--);
            }
        } else {
    
    
            objFormat(obj, level);
        }
    }

    /**
     * 只有对象才格式化数据
     *
     * @param obj
     * @param level
     */
    private static void objFormat(Object obj, int level) {
    
    
        for (Field field : obj.getClass().getDeclaredFields()) {
    
    
            try {
    
    
                if (isPrimitive(field.getType())) {
    
    
                    SensitiveValid sensitiveValid = field.getAnnotation(SensitiveValid.class);
                    if (sensitiveValid != null) {
    
    
                        ReflectionUtils.makeAccessible(field);
                        DesensitizationEnum desensitizationEnum = sensitiveValid.type();
                        Object fieldV = desensitizationEnum.getMethod().invoke(null, field.get(obj));
                        ReflectionUtils.setField(field, obj, fieldV);
                    }
                } else {
    
    
                    ReflectionUtils.makeAccessible(field);
                    Object fieldValue = ReflectionUtils.getField(field, obj);
                    if (fieldValue == null) {
    
    
                        continue;
                    }
                    formatMethod(fieldValue, level - 1);
                }
            } catch (Exception e) {
    
    
                log.error("脱敏数据处理异常", e);
            }
        }
    }

    /**
     * 基本数据类型和String类型判断
     *
     * @param clz
     * @return
     */
    public static boolean isPrimitive(Class<?> clz) {
    
    
        try {
    
    
            if (String.class.isAssignableFrom(clz) || clz.isPrimitive()) {
    
    
                return true;
            } else {
    
    
                return ((Class) clz.getField("TYPE").get(null)).isPrimitive();
            }
        } catch (Exception e) {
    
    
            return false;
        }
    }
}

The realization of desensitization AOP

Aop pluggable programming, in order to prevent unnecessary operations, so write a controllable class annotation EnableDesensitization

package com.lgh.common.sensitive;

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

/**
 * 方法返回值拦截器,需要注解才生效
 * @author lgh
 * @version 1.0
 * @date 2021/1/17
 **/
@Target({
    
    ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface EnableDesensitization {
    
    
}

Finally achieve intercept aop

package com.lgh.common.sensitive;

import com.lgh.common.utils.DesensitizationUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.context.annotation.Configuration;
import org.aspectj.lang.annotation.Aspect;
import java.lang.reflect.Method;

/**
 * @author lgh
 * @version 1.0
 * @date 2021/1/17
 */
@Aspect
@Configuration
public class SensitiveAspect {
    
    
    public static final String ACCESS_EXECUTION = "execution(* com.lgh.controller..*.*(..))";
    /**
     * 注解脱敏处理
     *
     * @param joinPoint
     * @return
     * @throws Throwable
     */
    @Around(ACCESS_EXECUTION)
    public Object sensitiveClass(ProceedingJoinPoint joinPoint) throws Throwable {
    
    
        return sensitiveFormat(joinPoint);
    }

    /**
     * 插拔式注解统一拦截器。@{link EnableDesensitization } 和 @SensitiveValid
     *
     * @param joinPoint
     * @return
     * @throws Throwable
     */
    public Object sensitiveFormat(ProceedingJoinPoint joinPoint) throws Throwable {
    
    
        Object obj = joinPoint.proceed();
        if (obj == null || DesensitizationUtils.isPrimitive(obj.getClass())) {
    
    
            return obj;
        }
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        EnableDesensitization desensitization = joinPoint.getTarget().getClass().getAnnotation(EnableDesensitization.class);
        if (desensitization != null || method.getAnnotation(EnableDesensitization.class) != null) {
    
    
            DesensitizationUtils.format(obj);
        }
        return obj;
    }
}

Actual combat drill

The UserDetail object I lived in the previous chapter added the phone field and added a comment, as follows:

package com.lgh.common.authority.entity;

import com.lgh.common.sensitive.DesensitizationEnum;
import com.lgh.common.sensitive.SensitiveValid;

public class UserDetail {
    
    
    private long id;
    private String name;
    @SensitiveValid(type = DesensitizationEnum.PHONE)
    private String phone;

    public long getId() {
    
    
        return id;
    }

    public void setId(long id) {
    
    
        this.id = id;
    }

    public String getName() {
    
    
        return name;
    }

    public void setName(String name) {
    
    
        this.name = name;
    }

    public void setPhone(String phone) {
    
    
        this.phone = phone;
    }

    public String getPhone() {
    
    
        return phone;
    }
}

Next, start the annotation in the controller

@GetMapping("/detail")
    @EnableDesensitization
    public IResult<UserDetail> getUser(@AuthenticationPrincipal UserDetail userDetail) {
    
    
        return CommonResult.successData(userDetail);
    }

You’re done, let’s implement the access operation
Insert picture description here

Links

github

Guess you like

Origin blog.csdn.net/soft_z1302/article/details/112756482