开发中对敏感字段进行脱敏方法

注解实现数据脱敏操作

在项目中经常使用到数据脱敏,这里使用 fasterxml.jackson来实现返回数据的脱敏操作

在这里借助 com.fasterxml.jackson.databind.JsonSerializer接口来完成对数据序列化的时候的操作

*引入包文件*

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-core</artifactId>
    <version>2.11.4</version>
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-annotations</artifactId>
    <version>2.11.4</version>
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.11.4</version>
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.datatype</groupId>
    <artifactId>jackson-datatype-jsr310</artifactId>
    <version>2.13.0</version>
</dependency>
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.76</version>
</dependency>

一、创建策略

在序列化的时候需要指定序列化的规则,例如

字段长度 大于 10 前三后四明文字段长度 小于 4 无明文字段长度 大其他 后四明文

这里使用工具类来实现这个通用的操作

public class SensitiveUtils {
    public static final String SYMBOL_STAR = "*";
    /**
     * 通用脱敏
     * 字段长度 大于 10 前三后四明文
     * 字段长度 小于 4 无明文
     * 字段长度 大其他 后四明文
     *
     * @param sensitiveValue 敏感值
     * @return 脱敏值
     */
    public static String commonSensitive(String sensitiveValue) {
        if (StringUtils.isBlank(sensitiveValue)) {
            return sensitiveValue;
        }
        if (sensitiveValue.length() > 10) {
            int s = sensitiveValue.length() - 7;
            return sensitiveValue.replaceAll("(\\w{3})\\w{" + s + "}(\\w{4})", "$1" + getStars(s) + "$2");
        } else if (sensitiveValue.length() < 4) {
            return getStars(sensitiveValue.length());
        } else {
            int s = sensitiveValue.length() - 4;
            return sensitiveValue.replaceAll("\\w{" + s + "}(\\w{4})", getStars(s) + "$1");
        }
    }
    /**
     * 判断是否是脱敏值
     * 是脱敏值 返回 true
     * 不是脱敏值 返回 false
     *
     * @param value 输入值
     * @return 返回结果
     */
    public static boolean simpleInvalidSensitive(String value) {
        if (StringUtils.isBlank(value)) {
            return false;
        }
        return value.contains(SYMBOL_STAR);
    }
    public static String getStars(int s) {
        String stars = StringUtils.EMPTY;
        for (int i = 0; i < s; i++) {
            stars += SYMBOL_STAR;
        }
        return stars;
    }
}

上面的是一个通用的工具类,下面,我们使用接口来实例化这个通用的策略:

首先是接口:


public interface MyStrategy {   
String desensitizationByPattern(String source);
}

//然后构建一个默认的策略:

public class MyDefaultStrategy implements MyStrategy {
    public String desensitizationByPattern(String source) {
        return SensitiveUtils.commonSensitive(source);
    }
}

这个操作就是传入一个字段返回脱敏后的字符串,到这里就完成了一个策略的制定

二、构建序列化注解

这个注解用来明确字段序列化时需要的序列化器和使用的策略,这里要用到注解 @JsonSerialize,它的参数 using用来明确使用的序列化器,这里使用自定义的序列化器,下一节介绍序列化器的创建。


@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotationsInside
@JsonSerialize(using = MySensitiveInfoSerialize.class)
@Inherited
public @interface CommonSensitive {
    /**
     * 脱敏策略
     *
     * @return
     */
    Class<? extends MyStrategy> strategy() default MyDefaultStrategy.class;
}

从上面可以看出来,这里的参数是一个接口性质的序列化策略,也就是说,后面是支持自定义的。

三、创建序列化器

序列化器要实现接口 com.fasterxml.jackson.databind.JsonSerializer

并实现方法


@Slf4j
@NoArgsConstructor
public class MySensitiveInfoSerialize extends JsonSerializer<String> implements
        ContextualSerializer {
    private MyStrategy myStrategy;
    public MySensitiveInfoSerialize(MyStrategy myStrategy) {
        this.myStrategy = myStrategy;
    }
    @Override
    public void serialize(String s, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
        jsonGenerator.writeString(myStrategy.desensitizationByPattern(s));
    }
    @Override
    public JsonSerializer<?> createContextual(SerializerProvider serializerProvider, BeanProperty beanProperty) throws JsonMappingException {
        if (beanProperty != null) {
            // 非 String 类直接跳过
            if (Objects.equals(beanProperty.getType().getRawClass(), String.class)) {
                CommonSensitive sensitiveInfo = beanProperty.getAnnotation(CommonSensitive.class);
                if (sensitiveInfo == null) {
                    sensitiveInfo = beanProperty.getContextAnnotation(CommonSensitive.class);
                }
                if (sensitiveInfo != null) {
                    Class<? extends MyStrategy> clazz = sensitiveInfo.strategy();
                    // 如果能得到注解,就将注解的 value 传入 SensitiveInfoSerialize
                    try {
                        return new MySensitiveInfoSerialize(clazz.getDeclaredConstructor().newInstance());
                    } catch (InstantiationException | IllegalAccessException | InvocationTargetException |
                             NoSuchMethodException e) {
                        throw new RuntimeException(e);
                    }
                }
                return serializerProvider.findValueSerializer(beanProperty.getType(), beanProperty);
            }
        }
        return serializerProvider.findNullValueSerializer(null);
    }
}

serialize使用序列化器序列化字段

createContextual创建序列化上下文,主要是明确使用的序列化策略或者其他方式

到这里基本就可以使用了

四、自定义序列化策略

从上面我们的策略是用接口的方式使用的,这里要实现这个接口


public class MyCustomizeNameStrategy implements MyStrategy {
    @Override
    public String desensitizationByPattern(String source) {
        if (StringUtils.isNotBlank(source)) {
            int s = source.length();
            int ss = source.lastIndexOf(".");
            if (ss == -1) {
                ss = 1;
                int sss = s - ss;
                return source.replaceAll("(\\S{" + ss + "})\\S{" + sss + "}", "$1" + SensitiveUtils.getStars(sss));
            } else {
                int sss = s - ss;
                return source.replaceAll("\\S{" + ss + "}(\\S{" + sss + "})", SensitiveUtils.getStars(ss) + "$1");
            }
        } else {
            return source;
        }
    }
}

这个是用来序列化姓名的,保留姓,这样就完成了,在使用注解 @CommonSensitive就可以使用了

五、规范化注解使用

在项目中经常要对不同性质的字段脱敏,例如邮箱,电话,身份证号码等,这些策略都不同,我们可以使用不同的序列化策略,但是这样使用不好的地方是我们要记住使用策略,为了方便使用,我们可以通过注解直接表明不同的注解表示不同的脱敏方式,无参的方式使用更加方便,这里选择使用邮件脱敏来说明

首先自定义一个邮件脱敏的规则:


public class MyEmailStrategy implements MyStrategy {
    @Override
    public String desensitizationByPattern(String source) {
        if (StringUtils.isNotBlank(source)) {
            int s = source.length();
            int ss = source.indexOf("@");
            int sss = s - ss;
            return source.replaceAll("\\S{" + ss + "}(\\S{" + sss + "})", SensitiveUtils.getStars(ss)+"$1");
        } else {
            return source;
        }
    }
}

然后通过使用我们定义的原始注解包装一下

@Documented@Target(ElementType.FIELD)@Retention(RetentionPolicy.RUNTIME)@CommonSensitive(strategy = MyEmailStrategy.class)@JacksonAnnotationsInsidepublic @interface MyEmailSensitive {}

这样就完成了对字段属性的序列化注解,使用的策略正是上面我们定义的,这个注解还是无参的。

六、使用案例

先建立一个序列化对象

@Documented
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@CommonSensitive(strategy = MyEmailStrategy.class)
@JacksonAnnotationsInside
public @interface MyEmailSensitive {
}
//上面我们使用不同的使用方式脱敏

//创建一个请求接口来实现脱敏操作

@RestController
@RequestMapping("/test")
@Slf4j
public class TestController {
    @PostMapping("/submit2")
    public Employee submit2(@RequestBody Employee employee) {
        log.info(JSON.toJSONString(employee));
        return employee;
    }
}

测试使用报文:

{    
  "name": "王斌强",
 "phone": "18637986236",    
 "email": "y.leffh@qymwmaej.yu"
}

打印日志:

{"email":"y.leffh@qymwmaej.yu","name":"王斌强","phone":"18637986236"}

返回报文:

{    
"name": "王*",    
"phone": "186****6236",    
"email": "*******@qymwmaej.yu"
}

至此,完成了使用注解的方式完成字段的脱敏操作。

### 如何在 SeaTunnel 中实现字段脱敏 SeaTunnel 是一个强大的数据集成工具,能够高效地处理大规模的数据同步、迁移和转换任务[^2]。为了满足安全性和隐私保护的需求,在实际应用中常常需要对敏感字段进行脱敏处理。 #### 使用内置函数进行简单脱敏操作 对于一些简单的场景,可以直接利用 SeaTunnel 提供的内置函数来完成基本的字符串替换或部分隐藏功能: ```sql SELECT id, CONCAT(SUBSTRING(phone_number, 1, 3), '****', SUBSTRING(phone_number, -4)) AS masked_phone, email_address FROM source_table; ``` 这段 SQL 查询语句展示了如何通过截取电话号码的不同片段组合成一个新的已遮蔽形式的结果列 `masked_phone`。 #### 自定义插件方式实现复杂逻辑 当面对更复杂的业务需求时,则可以通过编写自定义插件的方式来实现特定算法下的脱敏效果。这通常涉及到以下几个方面的工作: - **创建新的 Processor 插件**:基于官方文档指导开发适用于当前项目的处理器组件; - **配置参数传递机制**:允许用户灵活指定哪些列为待处理对象以及采用何种策略; - **注册并启用新模块**:按照框架规定的方法将自制的功能加入到整个工作流当中去。 具体来说就是参照 SeaTunnel 的扩展指南,构建自己的 Java 或 Scala 类文件,再经过打包上传至运行环境内即可生效使用。 #### 配置示例 下面给出一段 YAML 格式的作业配置样例,用于说明怎样设置输入输出连接器的同时指明要执行的变换动作(这里假设已经存在名为 MaskProcessor 的外部贡献者提交过的现成解决方案): ```yaml env { execution.parallelism = 1 } source { plugin_name = "Jdbc" ... } transform { processor.name = "MaskProcessor" parameters { fields_to_mask = ["ssn", "credit_card"] mask_pattern = "${prefix}***${suffix}" } } sink { plugin_name = "Elasticsearch" ... } ``` 在这个例子中,`fields_to_mask` 参数用来列举所有希望被掩盖掉的内容项名称列表,而 `mask_pattern` 则定义了一个模板串表示最终呈现出来的样式结构。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

极客叔

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值