1.注解类
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @Author: xiaoxin
* @Date: 2023/7/21 17:15
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD,ElementType.METHOD})
public @interface Encrypt {
Type type() default Type.MOBILE_PHONE;
enum Type {
//用户id
USER_ID,
//中文名
CHINESE_NAME,
//身份证号
ID_CARD,
//座机号
FIXED_PHONE,
//手机号
MOBILE_PHONE,
//地址
ADDRESS,
//电子邮件
EMAIL,
//密码
PASSWORD,
//中国大陆车牌,包含普通车辆、新能源车辆
CAR_LICENSE,
//银行卡
BANK_CARD,
//字符串
STR
}
}
2.EncryptUtil工具
import cn.hutool.core.util.DesensitizedUtil;
import io.ctc.commons.tools.utils.StringUtils;
import java.lang.reflect.Field;
/**
* @Author: xiaoxin
* @Date: 2023/7/21 17:16
*/
public class EncryptUtil {
public static void encryptFields(Object obj) {
Field[] fields = obj.getClass().getDeclaredFields();
for (Field field : fields) {
if (field.isAnnotationPresent(Encrypt.class)) {
Encrypt encryptAnnotation = field.getAnnotation(Encrypt.class);
Encrypt.Type type = encryptAnnotation.type();
field.setAccessible(true);
try {
String value = (String) field.get(obj);
String encryptedValue = encryptValue(type, value);
field.set(obj, encryptedValue);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
}
/***
* 根据类型进行脱敏处理
* @param type
* @param value
* @return
*/
public static String encryptValue(Encrypt.Type type, String value) {
StringBuffer sb = new StringBuffer();
switch (type) {
case USER_ID:
sb.append(DesensitizedUtil.userId());
break;
case CHINESE_NAME:
sb.append(DesensitizedUtil.chineseName(value));
break;
case ID_CARD:
sb.append(DesensitizedUtil.idCardNum(value, 1, 2));
break;
case FIXED_PHONE:
sb.append(DesensitizedUtil.fixedPhone(value));
break;
case MOBILE_PHONE:
sb.append(DesensitizedUtil.mobilePhone(value));
break;
case ADDRESS:
sb.append(DesensitizedUtil.address(value,6));
break;
case EMAIL:
sb.append(DesensitizedUtil.email(value));
break;
case PASSWORD:
sb.append(DesensitizedUtil.password(value));
break;
case CAR_LICENSE:
sb.append(DesensitizedUtil.carLicense(value));
break;
case BANK_CARD:
sb.append(DesensitizedUtil.bankCard(value));
break;
case STR:
if (StringUtils.isNotBlank(encryptionStr(value))){
sb.append(encryptionStr(value));
}
break;
}
return sb.toString();
}
/***
* 自定义字符串处理
* @param str
* @return
*/
public static String encryptionStr(String str) {
if (StringUtils.isBlank(str)){
return "";
}
int length = str.length();
if (length <= 4){
return str;
}
// 替换的起始位置
int startIndex = (length - 3) / 2;
// 替换的结束位置
int endIndex = startIndex + 3;
// 替换为4个*
String replacement = "****";
StringBuilder sb = new StringBuilder(str);
sb.replace(startIndex, endIndex, replacement);
return sb.toString();
}
}
3.EncryptAspect切面类
import io.ctc.commons.tools.utils.Result;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.Objects;
/**
* @author xiaoxin
*/
@Aspect
@Component
public class EncryptAspect {
protected Logger logger = LoggerFactory.getLogger(getClass());
@Pointcut("@annotation(io.util.test.Encrypt)")
public void encryptDataPointCut() {
}
/***
* 后置增强,返回数据脱敏切面类,页面上有部分数据是不能对外展示的,此方法不支持反脱敏
* 示例:原数据19879835555 脱敏后198****5555
* @param point
* @throws Throwable
*/
@AfterReturning(value = "encryptDataPointCut()",returning = "returnValue")
public void around(JoinPoint point, Object returnValue){
logger.info("---------------后置增强~~~~对部分数据脱敏---------------");
if (Objects.nonNull(returnValue)){
//1.判断object是否可以转换为List<?>类型
if (returnValue instanceof List<?>){
List list = (List) returnValue;
list.stream().forEach(a->{
EncryptUtil.encryptFields(a);
});
}else
//2.result对象,属性有code、msg、data
if (returnValue instanceof Result){
try{
Result result = (Result) returnValue;
Object data = result.getData();
if (Objects.nonNull(data)){
if (data instanceof List<?>){
List list = (List) data;
list.stream().forEach(a->{
EncryptUtil.encryptFields(a);
});
}else {
EncryptUtil.encryptFields(data);
}
}
}catch (ClassCastException e){
throw new ClassCastException("数据脱敏转换失败");
}
}else {
EncryptUtil.encryptFields(returnValue);
}
}
}
}
4.User对象(需要脱敏的属性)
import io.util.test.Encrypt;
import lombok.Data;
/**
* 测试
* @Author: xiaoxin
* @Date: 2023/7/21 17:16
*/
@Data
public class User {
@Encrypt(type = Encrypt.Type.USER_ID)
private String user_id;
@Encrypt(type = Encrypt.Type.CHINESE_NAME)
private String chinese_name;
@Encrypt(type = Encrypt.Type.ID_CARD)
private String id_card;
@Encrypt(type = Encrypt.Type.FIXED_PHONE)
private String fixed_phone;
@Encrypt(type = Encrypt.Type.MOBILE_PHONE)
private String mobile_phone;
@Encrypt(type = Encrypt.Type.ADDRESS)
private String address;
@Encrypt(type = Encrypt.Type.EMAIL)
private String email;
@Encrypt(type = Encrypt.Type.PASSWORD)
private String password;
@Encrypt(type = Encrypt.Type.CAR_LICENSE)
private String car_license;
@Encrypt(type = Encrypt.Type.BANK_CARD)
private String bank_card;
@Encrypt(type = Encrypt.Type.STR)
private String str;
}
5.下面为测试:
Controller
service
结果:
统一返回结果就上面这样子的,controller上打上注解,实体类上根据类型打上注解就可以了。可以支持返回对象、List集合,分页的谁要的话在切面里写一下就好了,DesensitizedUtil是hutool的。