自定义注解实现加解密及脱敏

1,604 阅读2分钟

title: ep_自定义注解实现加解密及脱敏 date: 2020-04-28 09:44


定义自定义注解

@Documented
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Order(Ordered.HIGHEST_PRECEDENCE)
public @interface PrivateData {

}
@Documented
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Order(Ordered.HIGHEST_PRECEDENCE)
public @interface PrivateDataMethod {

}

首先定义两个自定义注解,privateDataprivateDataMethod,分别将@Target属性定义为FIELDMETHOD

构造AOP逻辑

  • 申明一个切入点

      @Pointcut("@annotation(com.max.base.services.annotation.PrivateDataMethod)")
      public void annotationPointCut() {
      }

    对所有添加@privateDataMethod注解的方法进行切入。

  • 申明通知

    @Around("annotationPointCut()")
      public Object around(ProceedingJoinPoint joinPoint) {
          Object responseObj = null;
          try {
              Object[] request = joinPoint.getArgs();
              for (Object object : request) {
                  if (object instanceof Collection) {
                      Collection collection = (Collection) object;
                      collection.forEach(var -> {
                          try {
                              handleEncrypt(var);
                          } catch (IllegalAccessException e) {
                              e.printStackTrace();
                          }
                      });
                  } else {
                      handleEncrypt(object);
                  }
              }
              responseObj = joinPoint.proceed();
              if (responseObj instanceof Collection) {
                  Collection collection = (Collection) responseObj;
                  collection.forEach(var -> {
                      try {
                          handleDecrypt(var);
                      } catch (IllegalAccessException e) {
                          e.printStackTrace();
                      }
                  });
              } else {
                  handleDecrypt(responseObj);
              }
          } catch (Throwable throwable) {
              throwable.printStackTrace();
              log.error("SecureFieldAop 异常{}", throwable);
          }
          return responseObj;
      }

    申明Aroud通知,对于方法输入输出的对象进行判断,如果是非集合对象则直接进行加解密操作,否则则拆分集合,逐一操作

  • 处理加解密

    /**
     * 处理加密
     * @param requestObj
     */
    private void handleEncrypt(Object requestObj) throws IllegalAccessException {
        if (Objects.isNull(requestObj)) {
            return;
        }
        Field[] fields = requestObj.getClass().getDeclaredFields();
        for (Field field : fields) {
            boolean hasSecureField = field.isAnnotationPresent(PrivateData.class);
            if (hasSecureField) {
                Boolean accessible = field.isAccessible();
                if (!accessible) {
                    field.setAccessible(true);
                }
                String plaintextValue = (String) field.get(requestObj);
                String encryptValue = AseUtil.encrypt(plaintextValue, secretKey);
                field.set(requestObj, encryptValue);
                if (!accessible) {
                    field.setAccessible(false);
                }
            }
        }
    }

通过反射获取对象的Field列表,对于拥有@PrivateData注解的字段执行encryptValue()方法并用加密后的字符串覆盖原字段。 解密逻辑与加密类似,不做赘述。

测试

  • 标识insert()方法为需要加密的方法

    public interface CmTenantMapper {
      int deleteByPrimaryKey(Long id);
    
      @PrivateDataMethod
      int insert(CmTenant record);
    
      int insertSelective(CmTenant record);
    
      CmTenant selectByPrimaryKey(Long id);
    
      int updateByPrimaryKeySelective(CmTenant record);
    
      int updateByPrimaryKey(CmTenant record);
    }
  • 对传入对象中需要加密的字段添加注解

    public class CmTenant {
      private Long id;
      private String tenantId;
      @PrivateData
      private String tenantName;
      private String createBy;
      private Date createDate;
      private String updateBy;
      private Date updateDate;
      private String remarks;
      private Byte delFlag;
    //set get...
  • 调用insert方法查看数据保存结果 传入对象

    {
    "createBy": "可乐可不乐",
    "delFlag": "NOTDELETE",
    "remarks": "测试加密",
    "tenantId": "996",
    "tenantName": "椅子团队出品",
    "updateBy": "可乐可不乐"
    }

    数据库保存对象

  • 解密测试不做注释,大家自行尝试

脱敏逻辑

脱敏逻辑与加解密基本一致,需要注意的一点是脱敏的注解需要添加type类型

@Documented
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Order(Ordered.HIGHEST_PRECEDENCE)
public @interface MaskingField {
    MaskingTypeEnum type();
}

MaskingTypeEnum中定义脱敏的分类

public enum MaskingTypeEnum {
    /*身份证号码*/
    ID_CARD,
    /*手机号码*/
    PHONE,
    /*地址*/
    ADDRESS,
    /*姓名*/
    NAME
}

在使用是MaskingTypeEnum时标识字段的类型

    @MaskingField(type = MaskingTypeEnum.NAME)
    private String cpName;

后续~~bug~~功能大家自行研究, peace~

本文由博客一文多发平台 OpenWrite 发布!