若依原生框架中的脱敏功能不够灵活(默认超级管理员不脱敏,其他则脱敏)。
有时候,我们有些接口想要脱敏,但是有些接口又不想脱敏。(例如列表查询的时候脱敏。修改的时候,不想数据脱敏)
1、在com.ruoyi.common.config.serializer
中新建类SensitiveConditionSerializer
package com.ruoyi.common.config.serializer;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.BeanProperty;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.ContextualSerializer;
import com.ruoyi.common.annotation.SensitiveCondition;
import com.ruoyi.common.enums.DesensitizedType;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
/**
* 条件数据脱敏序列化器(新版,支持DesensitizedType)
*/
public class SensitiveConditionSerializer extends JsonSerializer<String> implements ContextualSerializer {
private SensitiveCondition sensitive;
private DesensitizedType desensitizedType;
public SensitiveConditionSerializer() {
}
public SensitiveConditionSerializer(SensitiveCondition sensitive, DesensitizedType desensitizedType) {
this.sensitive = sensitive;
this.desensitizedType = desensitizedType;
}
@Override
public void serialize(String value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
if (value == null) {
gen.writeNull();
return;
}
boolean needDesensitize = false;
Object currentObj = gen.getCurrentValue();
// 判断是否需要脱敏
if (sensitive != null) {
if (StringUtils.hasLength(sensitive.conditionMethod())) {
// 通过方法判断
Method method = ReflectionUtils.findMethod(currentObj.getClass(), sensitive.conditionMethod());
if (method != null) {
try {
needDesensitize = (boolean) ReflectionUtils.invokeMethod(method, currentObj);
} catch (Exception e) {
needDesensitize = false;
}
}
} else if (StringUtils.hasLength(sensitive.conditionField())) {
// 通过字段判断
Field field = ReflectionUtils.findField(currentObj.getClass(), sensitive.conditionField());
if (field != null) {
field.setAccessible(true);
try {
needDesensitize = (boolean) field.get(currentObj);
} catch (Exception e) {
needDesensitize = false;
}
}
}
}
// 根据条件决定是否脱敏
if (needDesensitize && desensitizedType != null) {
gen.writeString(desensitizedType.desensitizer().apply(value));
} else {
gen.writeString(value);
}
}
@Override
public JsonSerializer<?> createContextual(SerializerProvider prov, BeanProperty property) throws JsonMappingException {
if (property != null) {
SensitiveCondition anno = property.getAnnotation(SensitiveCondition.class);
if (anno == null) {
anno = property.getContextAnnotation(SensitiveCondition.class);
}
if (anno != null) {
// 从注解里拿type
DesensitizedType type = anno.type();
return new SensitiveConditionSerializer(anno, type);
}
}
return this;
}
}
2、在 com.ruoyi.common.annotation
中新建注解SensitiveCondition
package com.ruoyi.common.annotation;
import com.fasterxml.jackson.annotation.JacksonAnnotationsInside;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.ruoyi.common.config.serializer.SensitiveConditionSerializer;
import com.ruoyi.common.enums.DesensitizedType;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 条件数据脱敏注解
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotationsInside
@JsonSerialize(using = SensitiveConditionSerializer.class)
public @interface SensitiveCondition {
/**
* 脱敏类型
*/
DesensitizedType type();
/**
* 判断是否需要脱敏的方法名
* 该方法需要定义在实体类中,返回boolean类型
*/
String conditionMethod() default "";
/**
* 判断是否需要脱敏的字段名
* 该字段需要定义在实体类中,类型为boolean
*/
String conditionField() default "";
}
3、在 com.ruoyi.common.core.domain
的基类BaseEntity
中,添加属性needDesensitize
/** 是否需要脱敏 */
@JsonIgnore
private boolean needDesensitize = true;
public boolean isNeedDesensitize() {
return needDesensitize;
}
public void setNeedDesensitize(boolean needDesensitize) {
this.needDesensitize = needDesensitize;
}
4、在需要数据脱敏的实体类字段上方写注解
//eg: 用户类SysUser中
/** 用户昵称 */
@Excel(name = "用户名称")
@SensitiveCondition(type = DesensitizedType.USERNAME, conditionField = "needDesensitize")
private String nickName;
/** 用户邮箱 */
@Excel(name = "用户邮箱")
@SensitiveCondition(type = DesensitizedType.EMAIL, conditionField = "needDesensitize")
private String email;
/** 手机号码 */
@Excel(name = "手机号码", cellType = ColumnType.TEXT)
@SensitiveCondition(type = DesensitizedType.PHONE, conditionField = "needDesensitize")
private String phonenumber;
5、控制方式(使用方法)
// 默认情况下会脱敏(needDesensitize默认为true)
PersonInfo person = personInfoMapper.selectPersonInfoById(1L);
System.out.println(person.getName()); // 输出:张*丰
// 不需要脱敏的场景
person.setNeedDesensitize(false);
System.out.println(person.getName()); // 输出:张三丰
------------------------------------------------------
1、 在需要控制脱敏的地方,可以通过以下方式控制【通过字段控制】:
// 在Service层或Controller层
public List<SysUser> getPersonInfoList() {
List<SysUser> list = personInfoMapper.selectPersonInfoList();
// 某些情况下不需要脱敏
if (某些条件) {
list.forEach(person -> person.setNeedDesensitize(false));
}
return list;
}
2、通过方法控制(如果您选择使用conditionMethod)
@SensitiveCondition(type = DesensitizedType.USERNAME, conditionMethod = "shouldDesensitize")
private String name;
public boolean shouldDesensitize() {
// 根据业务逻辑决定是否脱敏,例如判断是否管理员
return !SecurityUtils.isAdmin();
}
---------------------------------------------------------------------------------
优点:
完全兼容原有的脱敏功能
可以灵活控制是否脱敏
支持多种控制方式(字段、方法)
易于扩展和维护
性能影响小,只在序列化时进行处理