乐趣区

关于spring:自定义注解实现加解密及脱敏


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

退出移动版