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 公布!