项目开发过程中有时需要对接口返回数据进行脱敏处理,目的是有一些重要数据比如:电话、邮箱、车牌、密码等等不能被用户访问,不要说网站需要登录外部用户没法访问,你要记住,在网络上任何数据都是不安全的。
之前的项目用的是拦截器模式做的脱敏,今天闲来无事做了一个稍微简单点的基于注解的脱敏处理工具。

下面的两个类是使用的拦截器方式,项目中可以选择使用,拦截器脱敏推荐看https://blog.csdn.net/a5932067/article/details/118970044,我这里做的是纯注解方式,不使用拦截器,写的不好,有需要改进的欢迎评论 。
一。创建注解
@Target({ElementType.FIELD,ElementType.METHOD})//必须是方法配合字段同时注解
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
/**
* 脱敏注解
* 使用流程:
* 1.注解返回方法 填写需要脱敏的类
* 2.注解需要脱敏的字段 填写规则
*
* @author: tq
* @description:
* @date: 2021/8/6 10:43
*/
public @interface Desensitized {
//脱敏类型(规则)
SensitiveTypeEnum type() default SensitiveTypeEnum.NO;
//被脱敏的class
Class<?> targetClass() default Void.class;
}
二。脱敏枚举
/**
* 脱敏枚举类 定义可被脱敏的类型
*
* @author: tq
* @description:
* @date: 2021/8/11 17:10
*/
public enum SensitiveTypeEnum {
/**
* 中文名
*/
CHINESE_NAME,
/**
* 身份证号
*/
ID_CARD,
/**
* 座机号
*/
FIXED_PHONE,
/**
* 手机号
*/
MOBILE_PHONE,
/**
* 地址
*/
ADDRESS,
/**
* 电子邮件
*/
EMAIL,
/**
* 银行卡
*/
BANK_CARD,
/**
* 密码
*/
PASSWORD,
/**
* 车牌号
*/
CARNUMBER,
/**
* 不需要脱敏 或者方法注册
*/
NO;
}
三。切面处理
/**
* 注解实际处理类 Map类型数据请绕道
*
* @author tq
* @since 2021年1月22日
*/
@Aspect
@Component
@Slf4j
public class DesensitizedHandler {
@Autowired
private ApplicationContext applicationContext;
@Pointcut("@annotation(填写注解位置)")
public void desensitizedPoint() {
}
@AfterReturning(returning = "data", pointcut = "desensitizedPoint()")
public Object around(JoinPoint joinPoint, Object data) throws Throwable {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
//获取方法的标签
Desensitized desensitized = signature.getMethod().getAnnotation(Desensitized.class);
//这里有局限性 只能是满足统一返回的类才能被用于检查
if (data instanceof ResponseMessage) {
ResponseMessage responseMessage = (ResponseMessage) data;
this.setFieldValueForCollection(desensitized, responseMessage.getResult());
} else {
return data;
}
return data;
}
/**
* 查询并赋值操作
*
* @param data 目标数据
*/
private void setFieldValueForCollection(Desensitized desensitized, Object data) throws Exception {
Class targetClass = desensitized.targetClass();
//确定不是集合 就直接获取字段
if (null == data) {
return;
}
Class clazz = data.getClass();
if (clazz == Field.class) {
clazz = ((Field) data).getType();
}
if (Collection.class.isAssignableFrom(clazz) || clazz.isArray()) {
//如果是集合 则循环获取
Collection collection = (Collection) data;
//这里不要判断空 因为 data已经判断过
if (collection.isEmpty()) {return;}
Iterator iterator = collection.iterator();
while (iterator.hasNext()) {
Object next = iterator.next();
setFieldValueForCollection(desensitized, next);
}
} else {
if (clazz != targetClass) {
return;
}
Field[] declaredFields = clazz.getDeclaredFields();
for (Field field : declaredFields) {
field.setAccessible(true);
//如果字段是空的 就不处理下一个
if (null == field.getType() || null == field.get(data)) {
continue;
}
//判断字段类型是否为集合
if (Collection.class.isAssignableFrom(field.getType()) || field.getType().isArray()) {
//如果是集合 则循环获取
Object dataIn = field.get(data);
setFieldValueForCollection(desensitized, dataIn);
} else {
Desensitized desensitization;
if (String.class != field.getType() || (desensitization = field.getAnnotation(Desensitized.class)) == null) {
continue;
}
field.setAccessible(true);// 暴力拆解
// String targetField = field.getName();// realName
String value = (String) field.get(data);
SensitiveTypeEnum type = desensitization.type();
//这里如果嫌长就放到util中
switch (type) {
case CHINESE_NAME:
value = DesensitizedUtils.chineseName(value);
break;
case ID_CARD:
value = DesensitizedUtils.idCardNum(value);
break;
case FIXED_PHONE:
value = DesensitizedUtils.fixedPhone(value);
break;
case MOBILE_PHONE:
value = DesensitizedUtils.mobilePhone(value);
break;
case ADDRESS:
value = DesensitizedUtils.address(value, 8);
break;
case EMAIL:
value = DesensitizedUtils.email(value);
break;
case BANK_CARD:
value = DesensitizedUtils.bankCard(value);
break;
case PASSWORD:
value = DesensitizedUtils.password(value);
break;
case CARNUMBER:
value = DesensitizedUtils.carNumber(value);
break;
default:
}
field.set(data, value);
}
}
}
}
}
四。使用
controller的方法上添加注解,并制定需要被脱敏的类,ResponseMessage是统一返回类,具体返回的数据一般都是被包装到内部。

具体返回类中的字段使用注解标明此字段需要以什么类型被脱敏。

主要流程为:利用@AfterReturning获取返回的数据-校验数据类型-循环校验-读取到被注解的字段-重新设置值。有一些局限性,如果需要不同项目使用的需要做一些小修改,后面有空再改改,看能不能改成通用的,欢迎大家给建议。
本文介绍了一种简单的基于注解的接口返回数据脱敏处理方法,用于保护敏感信息如电话、邮箱等。通过创建注解、枚举脱敏类型以及切面处理,实现了对响应数据中特定字段的脱敏。使用时只需在Controller方法和返回对象的字段上添加注解,即可自动处理敏感数据。
1254

被折叠的 条评论
为什么被折叠?



