网站首页/ 信息中心/ 档案百科/

数字档案馆系统敏感数据脱敏部署与配置实操

发布时间:2026年09月16日 08:15:22 浏览量:0

一、核心依赖引入与环境配置

本方案基于Java生态中最主流的MyBatis-Plus框架实现应用层动态脱敏,配合MySQL脚本处理存量数据。首先确保你的项目使用的是Spring Boot 2.x或3.x版本,并在pom.xml中引入必要的依赖。请直接复制以下坐标到你的pom文件中,不要遗漏版本号,以免引起兼容性冲突。

1. Maven依赖配置

在pom.xml的节点内添加以下内容:

``` com.baomidou mybatis-plus-boot-starter 3.5.3.1 org.projectlombok lombok 1.18.24 provided ```

添加依赖后,务必执行Maven刷新操作,确保jar包下载完整。接下来的步骤将直接基于这些依赖进行代码开发,无需安装额外的中间件或第三方服务,保持系统架构的轻量化。

二、自定义脱敏注解与工具类开发

为了实现灵活的脱敏控制,我们需要定义一个注解来标记实体类中需要脱敏的字段,并编写一个工具类处理具体的脱敏逻辑。这种方式侵入性最小,开发人员只需在实体字段上加注解即可。

1. 定义脱敏注解

创建名为DataMasking的注解类。该注解用于标记在实体类的字段上,指定脱敏类型。请完整复制以下代码:

``` package com.example.archive.annotation; import java.lang.annotation.; @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface DataMasking { / 脱敏类型枚举 / MaskingTypeEnum type(); } ```

同时创建对应的枚举类MaskingTypeEnum,定义数字档案馆常见的敏感数据类型:

``` package com.example.archive.annotation; public enum MaskingTypeEnum { // 身份证号:保留前3后4 ID_CARD, // 手机号:保留前3后4 PHONE_NUMBER, // 姓名:保留姓,名用代替 CHINESE_NAME, // 电子邮箱:保留首字母和@后缀 EMAIL } ```

2. 编写脱敏工具类

创建DataMaskingUtil类,实现具体的字符串截取和替换逻辑。这里必须处理空指针和字符串长度不足的情况,防止生产环境报错。

``` package com.example.archive.util; import com.example.archive.annotation.MaskingTypeEnum; public class DataMaskingUtil { public static String masking(String data, MaskingTypeEnum type) { if (data == null || data.isEmpty()) { return data; } switch (type) { case ID_CARD: return idCardMasking(data); case PHONE_NUMBER: return phoneMasking(data); case CHINESE_NAME: return nameMasking(data); case EMAIL: return emailMasking(data); default: return data; } } private static String idCardMasking(String idCard) { int len = idCard.length(); if (len < 8) return ""; return idCard.substring(0, 3) + "" + idCard.substring(len - 4); } private static String phoneMasking(String phone) { if (phone.length() != 11) return ""; return phone.substring(0, 3) + "" + phone.substring(7); } private static String nameMasking(String name) { if (name.length() == 1) return ""; return name.substring(0, 1) + "".repeat(Math.min(name.length() - 1, 2)); } private static String emailMasking(String email) { int index = email.indexOf("@"); if (index <= 1) return email; return email.charAt(0) + "" + email.substring(index); } } ```

三、基于MyBatis拦截器的动态脱敏实现

这是本方案的核心部分。通过实现MyBatis的Interceptor接口,拦截查询结果集,在数据返回给前端前进行实时替换。这种方式不需要修改数据库中的原始数据,保证了档案数据的完整性。

1. 创建结果集拦截器

创建ResultMaskingInterceptor类。该类会在SQL执行后、结果映射时触发。请确保签名注解正确指向ResultSetHandler

``` package com.example.archive.interceptor; import com.example.archive.annotation.DataMasking; import com.example.archive.util.DataMaskingUtil; import org.apache.ibatis.executor.resultset.ResultSetHandler; import org.apache.ibatis.plugin.; import org.apache.ibatis.reflection.MetaObject; import org.apache.ibatis.reflection.SystemMetaObject; import java.lang.reflect.Field; import java.util.List; import java.util.Properties; @Intercepts({ @Signature(type = ResultSetHandler.class, method = "handleResultSets", args = {Statement.class}) }) public class ResultMaskingInterceptor implements Interceptor { @Override public Object intercept(Invocation invocation) throws Throwable { // 执行原查询逻辑 Object result = invocation.proceed(); if (result instanceof List) { List list = (List) result; for (Object obj : list) { maskObject(obj); } } else { maskObject(result); } return result; } private void maskObject(Object obj) throws IllegalAccessException { if (obj == null) return; // 使用MyBatis元工具反射获取字段 MetaObject metaObject = SystemMetaObject.forObject(obj); // 获取原始对象的所有字段(包括父类) Class clazz = obj.getClass(); while (clazz != null) { for (Field field : clazz.getDeclaredFields()) { if (field.isAnnotationPresent(DataMasking.class)) { DataMasking annotation = field.getAnnotation(DataMasking.class); String fieldName = field.getName(); Object value = metaObject.getValue(fieldName); if (value instanceof String) { String maskedValue = DataMaskingUtil.masking((String) value, annotation.type()); metaObject.setValue(fieldName, maskedValue); } } } clazz = clazz.getSuperclass(); } } @Override public Object plugin(Object target) { return Plugin.wrap(target, this); } @Override public void setProperties(Properties properties) { } } ```

2. 注册拦截器配置

数字档案馆系统敏感数据脱敏部署与配置实操

在Spring Boot配置类中注册上述拦截器。如果项目已有MyBatis配置类,直接将addInterceptors方法内的代码加入即可。

``` package com.example.archive.config; import com.example.archive.interceptor.ResultMaskingInterceptor; import org.apache.ibatis.session.SqlSessionFactory; import org.springframework.context.annotation.Configuration; import org.springframework.beans.factory.annotation.Autowired; import javax.annotation.PostConstruct; import java.util.List; @Configuration public class MyBatisConfig { @Autowired private List sqlSessionFactoryList; @PostConstruct public void addInterceptor() { ResultMaskingInterceptor interceptor = new ResultMaskingInterceptor(); for (SqlSessionFactory sqlSessionFactory : sqlSessionFactoryList) { sqlSessionFactory.getConfiguration().addInterceptor(interceptor); } } } ```

四、存量档案数据SQL批量脱敏脚本

数字档案馆系统中往往已经存在大量历史数据。对于非开发人员访问(如报表导出、数据迁移)或需要物理脱敏的场景,需要直接在数据库层面执行更新。以下脚本针对MySQL数据库,涵盖了身份证、手机号和姓名的批量处理。

1. 备份与安全检查

在执行任何更新前,必须强制执行备份。以下命令创建一个结构相同的临时表用于备份:

``` CREATE TABLE archive_user_bak_20231027 LIKE archive_user; INSERT INTO archive_user_bak_20231027 SELECT FROM archive_user; ```

2. 执行脱敏更新

使用MySQL的字符串操作函数进行原地更新。请注意,执行前务必确认is_masked字段是否存在,如果不存在,建议先添加该字段标记是否已脱敏,防止重复执行导致数据彻底损坏。

``` -- 添加脱敏标记列(如果不存在) ALTER TABLE archive_user ADD COLUMN is_masked TINYINT(1) DEFAULT 0 COMMENT '是否已脱敏'; -- 批量脱敏身份证号 (保留前3后4) UPDATE archive_user SET id_card = CONCAT(LEFT(id_card, 3), '', RIGHT(id_card, 4)), is_masked = 1 WHERE is_masked = 0 AND LENGTH(id_card) >= 8; -- 批量脱敏手机号 (保留前3后4) UPDATE archive_user SET phone_number = CONCAT(LEFT(phone_number, 3), '', RIGHT(phone_number, 4)), is_masked = 1 WHERE is_masked = 0 AND phone_number REGEXP '^[0-9]{11}$'; -- 批量脱敏姓名 (保留首字) UPDATE archive_user SET real_name = CONCAT(LEFT(real_name, 1), ''), is_masked = 1 WHERE is_masked = 0 AND CHAR_LENGTH(real_name) > 1; ```

3. 处理JSON字段内的敏感数据

现代档案馆系统常使用JSON字段存储扩展信息。MySQL 5.7+支持JSON函数。假设ext_info字段中包含mobile键,更新脚本如下:

``` -- 更新JSON字段中的手机号 UPDATE archive_user SET ext_info = JSON_SET( ext_info, '$.mobile', CONCAT(LEFT(JSON_UNQUOTE(JSON_EXTRACT(ext_info, '$.mobile')), 3), '', RIGHT(JSON_UNQUOTE(JSON_EXTRACT(ext_info, '$.mobile')), 4)) ) WHERE is_masked = 0 AND JSON_CONTAINS_PATH(ext_info, 'one', '$.mobile'); ```

五、功能验证与效果确认

部署完成后,必须通过日志和实际返回值进行双重验证,确保脱敏规则生效且不影响业务逻辑。

1. 实体类标记测试

在你的用户实体类ArchiveUser中,对敏感字段添加注解:

``` public class ArchiveUser { private Long id; @DataMasking(type = MaskingTypeEnum.CHINESE_NAME) private String realName; @DataMasking(type = MaskingTypeEnum.ID_CARD) private String idCard; @DataMasking(type = MaskingTypeEnum.PHONE_NUMBER) private String phoneNumber; // getter and setter... } ```

2. 接口响应验证

启动项目,调用查询用户列表的接口。预期返回的JSON数据中,realName应显示为“张”,idCard显示为“1101234”,phoneNumber显示为“1385678”。

3. 数据库原始数据验证

执行SQL查询数据库:SELECT FROM archive_user WHERE id = 1;。如果配置的是应用层拦截器,这里查出的数据应当依然是明文,只有通过接口返回的才是密文。如果执行了第四步的SQL脚本,这里查出的就是密文。请根据你的实际安全需求选择保留明文(推荐)或物理脱敏。

档案归档总拖沓?数字化这剂解药你得服
档案归档总拖沓?数字化这剂解药你得服
哎,说到学校档案归档不及时这事儿,我太懂了。当年我在学校行政部门,那感觉就像是在一个永远在“明天再说”的漩涡里打转。学生档案、教学记录、行政文件……堆得跟小山似的,每次上级检查或者需要调个材料,那场面...
2026年09月16日 08:15:22
档案小白整理鉴定总踩坑?这3套实用圈定标准必看
档案小白整理鉴定总踩坑?这3套实用圈定标准必看
这事儿吧,很多刚接触档案的朋友,真的是抱着一堆纸/文件夹就敢直接上手,整理的时候凭感觉分堆,鉴定的时候看心情丢留,最后要么被领导骂“归档像垃圾场”,要么丢了重要材料哭唧唧。
2026年09月16日 08:15:22
铁岭档案培训怎么选?2024考证+实操上岗全指南
铁岭档案培训怎么选?2024考证+实操上岗全指南
现在铁岭本地不管是机关事业单位、国企还是规模以上私企,对档案合规的要求越来越严,很多刚接手档案工作的行政新人、想转行做稳定岗的朋友,都在找靠谱的学习渠道。不少人踩过坑:要么学的全是晦涩理论,上手整理的...
2026年09月16日 08:15:22
微信咨询
电话联系
QQ客服
微信咨询一对一服务
服务热线: 028-8744 4417
QQ客服: 2305721818