通常我们数据库中带有用户手机号的数据,在展示给前端的时候,都需要进行部分加密,也就是真实手机号为15066668888的数据在加密返回给前端之后,应为150****8888
关于这个问题,解决方案很多很灵活,这里我写一个使用自定义注解来加密手机号码字段的小栗子
基本思路
1.首先我们把数据从数据库中查询出来,放到实体对象中,在实体对象中需要被加密的字段上面加上我们自定义的注解
2.在业务层中,我们拿到实体对象或实体对象的集合,之后调用加密方法,把加了注解的字段进行加密,并且返回新的实体对象或新的实体对象集合
3.我们拿到新的结果之后,就可以按照之前的逻辑进行处理了。我自认为这样的操作对业务层的代码入侵不是很大
代码示例
1.自定义加密注解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface VirtualMobile {
String conditionField() default "";
}
2.注解处理器(这里写了静态方法,调用方法时执行加密操作)
public class VirtualMobileProcessor {
public static <T> T cryptoMobile(T object, Class<T> clazz) {
String cryptoField;
String conditionField;
for (Field declaredField : clazz.getDeclaredFields()) {
VirtualMobile annotation = declaredField.getAnnotation(VirtualMobile.class);
if (Validator.isNotNull(annotation)) {
cryptoField = declaredField.getName();
conditionField = annotation.conditionField();
return copyBean(cryptoField, conditionField, object, clazz);
}
}
return Convert.convert(clazz, object);
}
public static <T> Collection<T> cryptoMobileForList(Collection<T> collection, Class<T> clazz) {
return collection.stream().map(t -> cryptoMobile(t, clazz)).collect(Collectors.toList());
}
private static <T> T copyBean(String cryptoField, String conditionField, T object, Class<T> clazz) {
try {
if (Validator.isEmpty(cryptoField)) {
return Convert.convert(clazz, object);
}
if (Validator.isNotEmpty(conditionField)) {
for (Field declaredField : clazz.getDeclaredFields()) {
declaredField.setAccessible(true);
if (declaredField.getName().equals(conditionField) && Integer.valueOf(String.valueOf(declaredField.get(object))) == 0) {
return Convert.convert(clazz, object);
}
if (declaredField.getName().equals(conditionField) && Integer.valueOf(String.valueOf(declaredField.get(object))) == 1) {
return cryptoBean(cryptoField, object, clazz);
}
}
}
return cryptoBean(cryptoField, object, clazz);
} catch (Exception e) {
e.printStackTrace();
return Convert.convert(clazz, object);
}
}
private static <T> T cryptoBean(String cryptoField, T object, Class<T> clazz) {
try {
for (Field declaredField : clazz.getDeclaredFields()) {
declaredField.setAccessible(true);
if (declaredField.getName().equals(cryptoField)) {
BeanUtil.setFieldValue(object, cryptoField, createVirtualMobile(String.valueOf(declaredField.get(object))));
}
}
return Convert.convert(clazz, object);
} catch (Exception e) {
e.printStackTrace();
return Convert.convert(clazz, object);
}
}
private static String createVirtualMobile(String mobile) {
try {
Long.valueOf(mobile);
if (mobile.length() != 11) {
return mobile;
}
return StrUtil.hide(mobile, 3, 7);
} catch (Exception e) {
return mobile;
}
}
}
3.测试实体类
@Data
@NoArgsConstructor
@AllArgsConstructor
class Student {
private Long id;
@VirtualMobile(conditionField = "cryptoStatus")
private String mobile;
private Integer cryptoStatus;
private LocalDateTime createTime;
}
4.测试类
public class Test {
public static void main(String[] args) {
System.out.println(cryptoMobile(new Student(randomLong(11111111L, 99999999L), String.valueOf(randomLong(13011111111L, 13099999999L)),
1, LocalDateTime.now()), Student.class));
List<Student> studentList = new ArrayList<>();
for (int i = 0; i < 10; i++) {
Student student = new Student();
student.setId(randomLong(11111111L, 99999999L));
student.setMobile(String.valueOf(randomLong(13011111111L, 13099999999L)));
student.setCryptoStatus(1);
student.setCreateTime(LocalDateTime.now());
studentList.add(student);
}
long startMillis = System.currentTimeMillis();
Collection<Student> students = cryptoMobileForList(studentList, Student.class);
long stopMillis = System.currentTimeMillis();
System.out.println(stopMillis - startMillis + "ms");
System.out.println(students);
}
}
测试
测试类中,我们有单个对象的加密测试,也有集合的加密测试,我们一起跑一下

我们可以看到,手机号字段加密成功了
性能
我们测试一下加密10万数据所需要的时间
测试代码如下图

我测试五次分别是 267ms,241ms,254ms,272ms,257ms
我认为这样的处理速度还是可以接受的
以上就是使用自定义注解加密手机号码字段的全部示例,如果我的代码中有错误或您有更优的写法,欢迎大家吐槽