desensibilización de anotaciones java
Breve introducción
Con la popularidad de la era de Internet, la información del usuario se está volviendo cada vez más importante. También necesitamos desensibilizar y cifrar la información del usuario en el proceso de desarrollo de software. Para tareas más complicadas, explique personalmente cómo lograr la desensibilización de anotaciones. Admite llamadas estáticas y aop interceptación unificada para realizar la desensibilización o el retorno cifrado.
Explicación del código
Clase de enumeración de desensibilización
Defina la clase de enumeración, maneje toda la desensibilización y el cifrado, etc., mientras que es extensible, aquí solo hay un método de llamada anotado, para escribir muestras. DesensitizationEnum Si necesita otros métodos de desensibilización o cifrado, solo necesita agregar los siguientes tipos de enumeración
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;
}
}
Herramienta de desensibilización
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");
}
}
Escritura de clase de anotación
Esta clase se puede agregar al atributo de clase que debe desensibilizarse para lograr la desensibilización. Específicamente, la anotación se atraviesa de forma recursiva y la función de desensibilización se realiza a través del mecanismo de reflexión.
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();
}
Herramientas de desensibilización
Enunciado especial, cuando recurrimos, es una recursividad de índice y habrá un bucle infinito. Por ejemplo, un objeto se refiere a un objeto y se hace referencia a una dirección circular, por lo que habrá un bucle infinito. Hay 10 niveles de recursividad Por lo general, no permitimos objetos tan profundos.
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;
}
}
}
La realización de la desensibilización AOP
Aop programación enchufable, para evitar operaciones innecesarias, así que escriba una anotación de clase controlable 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 {
}
Finalmente lograr interceptar 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;
}
}
Ejercicio de combate real
El objeto UserDetail que viví en el capítulo anterior agregó el campo del teléfono y agregó un comentario, de la siguiente manera:
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;
}
}
A continuación, inicie la anotación en el controlador.
@GetMapping("/detail")
@EnableDesensitization
public IResult<UserDetail> getUser(@AuthenticationPrincipal UserDetail userDetail) {
return CommonResult.successData(userDetail);
}
Listo, implementemos la operación de acceso