Young Kbt blog Young Kbt blog
首页
  • java基础

    • Java基础
    • Java集合
    • Java反射
    • JavaJUC
    • JavaJVM
  • Java容器

    • JavaWeb
  • Java版本新特性

    • Java新特性
  • SQL 数据库

    • MySQL
    • Oracle
  • NoSQL 数据库

    • Redis
    • ElasticSearch
  • 数据库

    • MyBatis
    • MyBatis-Plus
  • 消息中间件

    • ActiveMQ
    • RabbitMQ
    • RocketMQ
    • Kafka
  • 进阶服务

    • Nginx
  • Spring
  • Spring Boot
  • Spring Security
  • 设计模式
  • 算法
  • 知识
  • 管理

    • Maven
    • Git
  • 部署

    • Linux
    • Docker
    • Jenkins
    • Kubernetes
  • 进阶

    • TypeScript
  • 框架

    • React
    • Vue2
    • Vue3
  • 轮子工具
  • 项目工程
  • 友情链接
  • 本站

    • 分类
    • 标签
    • 归档
  • 我的

    • 收藏
    • 关于
    • Vue2-Admin (opens new window)
    • Vue3-Admin(完善) (opens new window)
GitHub (opens new window)

Shp Liu

朝圣的使徒,正在走向编程的至高殿堂!
首页
  • java基础

    • Java基础
    • Java集合
    • Java反射
    • JavaJUC
    • JavaJVM
  • Java容器

    • JavaWeb
  • Java版本新特性

    • Java新特性
  • SQL 数据库

    • MySQL
    • Oracle
  • NoSQL 数据库

    • Redis
    • ElasticSearch
  • 数据库

    • MyBatis
    • MyBatis-Plus
  • 消息中间件

    • ActiveMQ
    • RabbitMQ
    • RocketMQ
    • Kafka
  • 进阶服务

    • Nginx
  • Spring
  • Spring Boot
  • Spring Security
  • 设计模式
  • 算法
  • 知识
  • 管理

    • Maven
    • Git
  • 部署

    • Linux
    • Docker
    • Jenkins
    • Kubernetes
  • 进阶

    • TypeScript
  • 框架

    • React
    • Vue2
    • Vue3
  • 轮子工具
  • 项目工程
  • 友情链接
  • 本站

    • 分类
    • 标签
    • 归档
  • 我的

    • 收藏
    • 关于
    • Vue2-Admin (opens new window)
    • Vue3-Admin(完善) (opens new window)
GitHub (opens new window)
  • 设计模式

  • 算法

  • 知识

    • 知识 - 对象
    • 知识 - 幂等性
    • 知识 - 分布式事务
    • 知识 - MapStruct
    • 知识 - MapStructPlus
    • 知识 - CompletableFuture
    • 知识 - EasyExcel
    • 知识 - Encrypt
    • 知识 - 接口幂等性
    • 知识 - 数据脱敏
      • 什么是数据脱敏
      • 实现数据脱敏
        • 注解
        • 脱敏策略
        • 脱敏处理器
        • 自定义脱敏服务
      • 示例
    • 知识 - WebSocket
    • 知识 - Spring Cache
    • 知识 - 请求日志输出
    • 知识 - 接口限流
  • 开发
  • 知识
Young Kbt
2024-06-15
目录

知识 - 数据脱敏

  • 什么是数据脱敏
  • 实现数据脱敏
    • 注解
    • 脱敏策略
    • 脱敏处理器
    • 自定义脱敏服务
  • 示例

# 什么是数据脱敏

数据脱敏(Data Masking)是一种信息安全技术,旨在保护敏感信息和隐私数据,防止未经授权的访问或泄露。它通过对原始数据进行有策略的修改或替换,创建一个看上去与原数据相似但不含真正敏感细节的数据副本,以供非生产环境如开发、测试、分析或培训等用途中安全使用。

数据脱敏的目的:

  1. 保护隐私:确保个人信息如身份证号、电话号码、银行账号等不被非法获取和利用

  2. 合规要求:满足行业规范和法律法规对数据保护的要求,如GDPR(欧盟通用数据保护条例)等

  3. 安全测试:在不影响真实数据安全的前提下,为软件测试、系统调试提供接近真实的测试数据

  4. 降低风险:即便数据被非法访问,由于已脱敏,实际敏感信息不会泄露,降低了数据泄露的风险

数据脱敏的应用在生活中是比较常见的,比如我们在淘宝买东西订单详情中,商家账户信息会被用 * 遮挡,保障了商户隐私不泄露,这就是一种数据脱敏方式。

数据脱敏又分为静态数据脱敏(SDM)和 动态数据脱敏(DDM):

静态数据脱敏与动态数据脱敏:

  • 静态数据脱敏:在数据被提取并复制到非生产环境之前一次性完成脱敏处理。适用于数据外发场景,如提供给第三方或用于测试数据库

  • 动态数据脱敏:在数据查询过程中实时进行,当用户访问敏感数据时,系统自动对其进行脱敏处理。适用于直接连接生产数据库的场景,确保即使查看数据的行为也不会暴露敏感信息

# 实现数据脱敏

实现数据脱敏步骤:

  • 提供 Sensitive 注解,在实体类的属性上使用,注解里可以指定脱敏的类型
  • 自定义类继承和实现 jackson 提供的 JsonSerializer、ContextualSerializer,这样在返回数据时,会自动触发自定义类的指定方法,将带有 Sensitive

注解的属性进行自动传入(Sensitive 注解使用了 JsonSerialize 的 use 实现自动传入)

脱敏支持的类型请看 SensitiveStrategy 枚举类。主要利用了 hutool 的脱敏方法。

模块支持自定义脱敏类型,实现 SensitiveService 接口,主要基于用户权限来决定是否脱敏并返回。

依赖

<dependencies>
  <dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.32</version>
  </dependency>
  <dependency>
    <groupId>cn.hutool</groupId>
    <artifactId>hutool-core</artifactId>
  </dependency>
</dependencies>
1
2
3
4
5
6
7
8
9
10
11

# 注解

数据脱敏注解,标注属性后,会创建一个 SensitiveHandler 的序列化器

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@JacksonAnnotationsInside
@JsonSerialize(using = SensitiveHandler.class)
public @interface Sensitive {

    /**
     * 脱敏规则
     */
    SensitiveStrategy strategy();

    /**
     * UAC 角色码
     */
    String roleCode() default "";

    /**
     * UAC 菜单权限码
     */
    String perms() default "";

    /**
     * 前置不需要打码的长度,仅 strategy 为 SensitiveStrategy.CUSTOMIZE_RULE 生效
     */
    int startLen() default 0;

    /**
     * 后置不需要打码的长度,仅 strategy 为 SensitiveStrategy.CUSTOMIZE_RULE 生效
     */
    int endLen() default 0;

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

# 脱敏策略

脱敏策略,采用 hutool 的 DesensitizedUtil 工具类实现脱敏

@AllArgsConstructor
@Getter
public enum SensitiveStrategy {

    /**
     * 默认脱敏
     */
    DEFAULT(value -> value),

    /**
     * 自定义规则脱敏
     */
    CUSTOMIZE_RULE(value -> value),

    /**
     * 中文名脱敏
     */
    CHINESE_NAME(DesensitizedUtil::chineseName),

    /**
     * 密码脱敏
     */
    PASSWORD(DesensitizedUtil::password),

    /**
     * 身份证脱敏
     */
    ID_CARD(s -> DesensitizedUtil.idCardNum(s, 3, 4)),

    /**
     * 手机号脱敏
     */
    PHONE(DesensitizedUtil::mobilePhone),

    /**
     * 地址脱敏
     */
    ADDRESS(s -> DesensitizedUtil.address(s, 8)),

    /**
     * 邮箱脱敏
     */
    EMAIL(DesensitizedUtil::email),

    /**
     * 银行卡
     */
    BANK_CARD(DesensitizedUtil::bankCard);

    private final Function<String, String> desensitize;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51

# 脱敏处理器

实现 ContextualSerializer 初始化一个序列化器,继承 JsonSerializer 实现序列化功能。Spring MVC 返回给前端的时候,会调用继承 JsonSerializer 的自定义类处理。

@Slf4j
public class SensitiveHandler extends JsonSerializer<String> implements ContextualSerializer {
    private SensitiveStrategy strategy;
    private int startLen;
    private int endLen;
    private String roleCode;
    private String perms;

    /**
     * 执行序列化操作
     */
    @Override
    public void serialize(String value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
        try {
            // 默认规则
            if (SensitiveStrategy.DEFAULT.equals(strategy)) {
                gen.writeString(value);
                return;
            }
            // 自定义规则
            if (SensitiveStrategy.CUSTOMIZE_RULE.equals(strategy)) {
                gen.writeString(StrUtil.hide(value, startLen, endLen));
                return;
            }
            // 如果传入了 roleCode 和 perms,代表走自定义 Service 实现类来进行校验,返回 true 开启脱敏
            SensitiveService sensitiveService = SpringHelper.getBean(SensitiveService.class);
            if (StringUtil.hasAnyText(roleCode, perms) && Objects.nonNull(sensitiveService)) {
                if (sensitiveService.isSensitive(roleCode, perms)) {
                    gen.writeString(strategy.getDesensitize().apply(value));
                } else {
                    // 返回 false 不序列化
                    gen.writeString(value);
                }
            } else {
                gen.writeString(strategy.getDesensitize().apply(value));
            }
        } catch (BeansException e) {
            log.error("脱敏实现不存在, 采用默认处理 => {}", e.getMessage());
            gen.writeString(value);
        }
    }

    /**
     * 项目初始化后,获取注解信息进行存储,每一个注解对应一个类实例
     */
    @Override
    public JsonSerializer<?> createContextual(SerializerProvider prov, BeanProperty property) throws JsonMappingException {
        Sensitive annotation = property.getAnnotation(Sensitive.class);
        if (Objects.nonNull(annotation) && Objects.equals(String.class, property.getType().getRawClass())) {
            this.strategy = annotation.strategy();
            this.startLen = annotation.startLen();
            this.endLen = annotation.endLen();
            this.roleCode = annotation.roleCode();
            this.perms = annotation.perms();
            return this;
        }
        return prov.findValueSerializer(property.getType(), property);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59

# 自定义脱敏服务

public interface SensitiveService {
    /**
     * 是否脱敏
     */
    boolean isSensitive(String roleKey, String perms);
}
1
2
3
4
5
6

# 示例

@RestController
@RequestMapping("/demo/sensitive")
public class DemoSensitiveController {


    @RequestMapping("/test")
    public Response<TestSensitive> test() {
        TestSensitive testSensitive = new TestSensitive();
        testSensitive.setMessage("端午节放假啦");
        testSensitive.setRuleMessage("123456789");
        testSensitive.setIdCard("210397198608215431");
        testSensitive.setPhone("15777815847");
        testSensitive.setAddress("深圳市龙岗区某某小区1327室");
        testSensitive.setEmail("2456019588@qq.com");
        testSensitive.setBankCard("6226456952351452853");

        return HttpResult.ok(testSensitive);
    }

    @Data
    static class TestSensitive {

        /**
         * 不脱敏消息
         */
        @Sensitive(strategy = SensitiveStrategy.DEFAULT)
        private String message;

        /**
         * 自定义规则脱敏
         */
        @Sensitive(strategy = SensitiveStrategy.CUSTOMIZE_RULE, startLen = 3, endLen = 6)
        private String ruleMessage;

        /**
         * 身份证
         */
        @Sensitive(strategy = SensitiveStrategy.ID_CARD)
        private String idCard;

        /**
         * 电话
         */
        @Sensitive(strategy = SensitiveStrategy.PHONE, roleCode = "admin")
        private String phone;

        /**
         * 地址
         */
        @Sensitive(strategy = SensitiveStrategy.ADDRESS, perms = "system:user:query")
        private String address;

        /**
         * 邮箱
         */
        @Sensitive(strategy = SensitiveStrategy.EMAIL, roleCode = "admin", perms = "system:user:query")
        private String email;

        /**
         * 银行卡
         */
        @Sensitive(strategy = SensitiveStrategy.BANK_CARD, roleCode = "visitor", perms = "system:user:query")
        private String bankCard;

    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
编辑此页 (opens new window)
#knowledge
更新时间: 2024/06/15, 16:39:27
知识 - 接口幂等性
知识 - WebSocket

← 知识 - 接口幂等性 知识 - WebSocket→

最近更新
01
技术随笔 - Element Plus 修改包名 原创
11-02
02
Reactor - 扩展性
11-02
03
Reactor - 最佳实践
11-02
更多文章>
Theme by Vdoing | Copyright © 2021-2024 Young Kbt | blog
桂ICP备2021009994号
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式