在后台管理中,对于手机号,身份证,姓名这些数据不允许所有人都能看,这时候我们要对相对数据进行脱敏.
先声明了一个注解, 通过对相关接口函数进行声明,以及配置需要脱敏的参数类型SecretTypeEnum
,默认脱敏手机号
/**
* 脱敏声明
*/
@Documented
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface SecretManage {
SecretTypeEnum[] value() default {SecretTypeEnum.MOBILE};
}
我们目前只支持对手机号,身份证,用户姓名三个字段进行脱敏, 字段名称必须符合枚举的desc值
package com.test.base.enums;
import com.fasterxml.jackson.annotation.JsonValue;
/**
* @author fyf
* @date 2021/2/26
*/
public enum SecretTypeEnum implements BaseEnum {
MOBILE(0, "mobile"),
NAME(1, "name"),
ID(2, "identity")
;
@JsonValue
private int code;
private String desc;
public String getDesc() {
return desc;
}
SecretTypeEnum(int code, String desc) {
this.code = code;
this.desc = desc;
}
@Override
public int code() {
return code;
}
public static SecretTypeEnum checkParam(String paramName) {
SecretTypeEnum[] values = values();
for (int i = 0; i < values.length; i++) {
if (paramName.equals(values[i].getDesc())) {
return values[i];
}
}
return null;
}
}
然后我们需要实现注解的拦截功能
/**
* 对字段进行脱敏管理
* @author: fyf
* @date: 2021/02/23
*/
@Order(1)
@Aspect
@Component
public class SecretManageAspect {
@Pointcut("@annotation(com.test.base.annotations.SecretManage)")
public void pointCut() {
}
@Around("pointCut() && @annotation(secretManage)")
public Object secretManageAround(ProceedingJoinPoint joinPoint, SecretManage secretManage) throws Throwable {
Class returnType = ((MethodSignature) joinPoint.getSignature()).getReturnType();
Object invokeResult = joinPoint.proceed();
if (returnType.isInstance(invokeResult)) {
returnType.cast(invokeResult);
}
Field[] fields = returnType.getDeclaredFields();
List<SecretTypeEnum> annotationSecretTypeEnums = Arrays.asList(secretManage.value());
for (Field field : fields) {
String fieldName = field.getName();
Class<?> paramType = field.getType();
System.out.println("字段名称:" + fieldName);
SecretTypeEnum secretTypeEnum = SecretTypeEnum.checkParam(fieldName);
long count = annotationSecretTypeEnums.stream().filter(item -> item.equals(secretTypeEnum)).count();
if (secretTypeEnum != null && count > 0) {
fieldName = fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
// 获取到setter方法
Method setMethod = returnType.getMethod("set" + fieldName, new Class[]{paramType});
// 获取到getter方法
Method getMethod = returnType.getMethod("get" + fieldName);
// 执行getter方法
Object value = getMethod.invoke(invokeResult);
this.invokeSetter(setMethod, invokeResult, secretTypeEnum, value);
}
}
return invokeResult;
}
/**
* 封装执行setter函数
*/
private void invokeSetter(Method setterMethod, Object invokeResult, SecretTypeEnum secretTypeEnum, Object value)
throws InvocationTargetException, IllegalAccessException {
switch (secretTypeEnum) {
case NAME:
setterMethod.invoke(invokeResult, SecretUtil.nameSecret(value.toString()));
break;
case MOBILE:
setterMethod.invoke(invokeResult, SecretUtil.mobileSecret(value.toString()));
break;
case ID:
setterMethod.invoke(invokeResult, SecretUtil.idNoSecret(value.toString()));
break;
}
}
}
上面我们就是实现了脱敏的功能,现在我们可以mock接口看看结果了, 我对默认声明和脱敏名称和手机号进行了测试.
/**
* curl localhost:9999/user/test-secret
*/
@GetMapping("/test-secret")
// @SecretManage(value = {SecretTypeEnum.NAME, SecretTypeEnum.MOBILE})
@SecretManage
public User getSecretUser() {
User user = new User();
user.setId(1);
user.setMobile("13715166409");
user.setName("张志新");
user.setIdentity("370283790911703");
return user;
}
下面是测试结果
好了,今天的脱敏功能实践分享就到这里