1. 等保三级整改里,敏感数据加密为什么总卡在“改不完”
等保三级复测的整改清单里,敏感数据加密几乎是必选项:手机号、身份证号、银行卡号、家庭住址,只要落库就得加密。算法本身不难,AES-256 是成熟方案,难的是“改哪些、怎么改、存量怎么办”。我们这次面对的是数十个遗留系统、十几个数据库、上百张表、十几个独立代码仓库,整改窗口只有一周,团队三个人。
真正拖慢进度的从来不是加密算法,而是三件事:第一,敏感字段散落在上百张表里,不逐张翻就不知道哪张有;第二,存量明文有几百万条,服务不能停,新老格式必须兼容;第三,系统之间共用用户数据,A 系统改了加密逻辑,B 系统的查询可能直接挂掉。这三件事叠加,人工摸排加改造,保守估计要两周以上。
这篇把我们实际跑通的路径完整写出来:用 Cursor 做批量扫描和重复代码生成,把加解密收拢到 MyBatis 拦截器一层,业务代码只加注解。你可以直接复制提示词模板、拦截器骨架和逐系统验证清单,目标是一周内完成整改,并留下可审计的验证记录。适合中小团队在数十个遗留系统上快速补齐 AES-256 加密能力。
2. 前置准备:TaoToken 接入与 Cursor 里的模型配置
批量扫描字段、生成拦截器骨架、写迁移脚本,这些活都需要一个稳定的模型调用入口。我们用的是 TaoToken,它提供 OpenAI 兼容接口,在 Cursor 里配置成自定义模型即可,不用改代码里的调用方式。
先拿到 API Key。打开 https://taotoken.net/api-keys ,登录后创建一个 Key,复制保存。注意 Key 只在创建时完整显示一次,丢了就重新建一个。
然后在 Cursor 里配置。打开 Settings,找到 Models 面板,添加一个 OpenAI 兼容的模型提供方:
{ "modelProvider": "openai", "apiKey": "你的TaoToken API Key", "baseUrl": "https://taotoken.net/api/v1", "models": [ "claude-sonnet-4-20250514", "gpt-4o" ] }配置完成后,在 Cursor 的 Chat 面板里选这个模型,发一句“你好”验证连通。如果返回正常,说明接入成功。这一步建议在第一天上午做完,后面所有扫描和生成都依赖它。
注意:baseUrl 末尾要带
/v1,这是 OpenAI 兼容接口的约定路径。少写这一段,Cursor 会报 404。
如果你更习惯在网页里直接对话验证模型效果,可以打开 https://taotoken.net/model-chat 先试几轮,确认模型对 Java、MyBatis 这类技术问题的回答质量符合预期,再回到 Cursor 里批量用。
3. 第一天:用 Cursor 批量扫描,把“要改哪些”摸清楚
3.1 数据库层:导出字段清单,让模型做语义识别
第一步不是写代码,是把所有表的字段名导出来。写一个脚本连接十几个数据库,把库名.表名.字段名全部导出成文本。然后把这批文本喂给 Cursor,用下面这个提示词:
以下是我们所有数据库的表字段清单,请识别出可能包含敏感信息的字段。 敏感信息包括:手机号、身份证号、银行卡号、姓名、地址、邮箱、IP地址。 请按“数据库名.表名.字段名”的格式列出,并标注敏感类型。 [粘贴字段清单]半小时左右能拿到一张疑似清单。关键是人工逐条确认,模型会误判,比如user_card_type存的是会员卡类型,不是银行卡号。我们最终确认涉及敏感字段的表 87 张,字段 143 个,比预估多不少。如果靠人工逐张翻,这一步就要两三天,而且不敢保证不漏。
3.2 代码层:按五个类型归类使用位置
有了字段清单,还要知道每个字段在代码里被哪些地方读写。用 Cursor 逐个仓库做全局搜索,搜索维度是字段名、实体类名、以及 WHERE 条件里用到敏感字段的 SQL。每扫一个仓库,让模型把结果归成五类:
| 类型 | 场景 | 处理方式 |
|---|---|---|
| A | INSERT/UPDATE 写入 | 写入前加密 |
| B | SELECT 读取后处理 | 读取后解密 |
| C | WHERE 条件中使用 | 需确定性加密索引 |
| D | 日志打印中出现 | 脱敏 |
| E | 接口返回值直接透传 | 脱敏或加密传输 |
类型 C 最麻烦。加密之后WHERE phone = '138xxxx'就没法用了,数据库里存的是密文,不能用明文比对。我们的处理是:用于查询的敏感字段额外维护一个确定性加密索引,同样的明文始终加密成同样的密文,专门用于查询;存储字段用随机 IV 的 AES,保证安全性。这个决策必须在第一天定下来,否则后面改到一半返工。
3.3 汇总成改造清单,按系统分工
扫完所有仓库,得到一张完整清单,标注每个系统涉及哪些库、哪些表、哪些字段、哪些仓库要改。到这里“要改哪些”才真正清楚。整个摸排花了一天,纯人工保守估计三到四天。
4. 第一天下午到第二天:把加解密收拢到 MyBatis 拦截器
4.1 核心决策:不在每个 Service 里手动加解密
143 个字段散在数十个系统、几十个 Service 里,如果每个读写点手动加解密,要改几百处,改一个漏一个,上线必出事。我们选的方案是在 MyBatis 层做统一拦截,注解驱动,业务层无感知。业务代码唯一要动的,就是在实体类字段上加一个注解:
@SensitiveField(type = EncryptType.RANDOM) private String phone;前期多花一天搭基础设施,后面 143 个字段的改造成本会低到难以置信。改造分三阶段:基础设施搭建、字段改造、存量数据迁移。顺序想清楚,三个人才能并行不互相踩。
4.2 加密工具类:随机 IV 与确定性双模式
给 Cursor 的提示词:
帮我实现一个 AES-256 加密工具类,要求: 1. 支持两种模式: - 随机 IV 模式(用于存储,同明文每次密文不同) - 确定性模式(用于查询索引,同明文始终同密文) 2. 密钥从配置中心读取,支持密钥轮换(同时支持新旧两个密钥解密) 3. 加密结果 Base64 编码,方便数据库存储 4. 加密结果加固定前缀标识(用于迁移期间区分明文和密文) 5. 所有异常封装成业务异常,不能把加密细节暴露给上层 6. 技术栈:Java 17 + Spring Boot 3生成后重点 review 两处:密钥轮换的优先级顺序,以及异常日志不能打印原始数据。这两处模型容易写得不严谨。
4.3 MyBatis 拦截器骨架
@Intercepts({ @Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class}), @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}) }) public class SensitiveFieldInterceptor implements Interceptor { private final Map<Class<?>, List<Field>> fieldCache = new ConcurrentHashMap<>(); @Override public Object intercept(Invocation invocation) throws Throwable { Object[] args = invocation.getArgs(); MappedStatement ms = (MappedStatement) args[0]; Object parameter = args[1]; if (ms.getSqlCommandType() == SqlCommandType.INSERT || ms.getSqlCommandType() == SqlCommandType.UPDATE) { encryptFields(parameter); } Object result = invocation.proceed(); if (ms.getSqlCommandType() == SqlCommandType.SELECT) { decryptResult(result); } return result; } private void encryptFields(Object parameter) { // 遍历参数对象,命中 @SensitiveField 的字段按类型加密 // RANDOM 用随机 IV,DETERMINISTIC 用确定性模式 } private void decryptResult(Object result) { // 处理单对象、List、以及 MyBatis-Plus 分页结果 // 读取时判断是否有加密前缀,无前缀直接返回明文(兼容期) } }生成后重点看两个地方:反射缓存的并发安全性,用ConcurrentHashMap没问题;分页结果的解密是否遗漏,测几个边界 case。下午三点基础设施完成,单元测试覆盖率 83%。在测试库的实体类加一个注解,写进去是密文,读出来是明文,查询和分页都正常。
5. 第三天到第五天:数十个系统并行改造
基础设施就位后,三个人按系统并行,每个字段的标准流程固定下来:
- 在实体类字段加
@SensitiveField注解,约 1 分钟一个字段。 - 检查该字段是否用于 WHERE 查询,如果是,同步处理查询索引字段。
- 检查日志打印是否包含该字段,如果是,加脱敏。
- 检查接口返回值是否直接透传,如果是,确认是否需要脱敏。
- 跑单元测试。
- 跑集成测试,含存量数据兼容性验证。
Cursor 在这里的价值不是生成复杂逻辑,而是批量处理高度重复的工作。把实体类喂给模型,让它按清单加注解、处理边界,人来 review。单个实体类平均用时从估计的 30 分钟压到 8 分钟。143 个字段三人并行,三天全部改完,集成测试通过。
中间踩的一个坑:有个老系统的接口把手机号直接拼进 SQL 字符串:
String sql = "SELECT * FROM user WHERE phone = '" + phone + "'";这种情况拦截器拦不到。让 Cursor 在该系统的所有仓库里搜类似写法,找到 5 处,全部改成参数化查询,同时加上查询时的加密转换。这 5 处如果靠人工逐文件找,很可能漏掉。
6. 第六天:存量数据迁移,最危险的一步
几百万条明文散在十几个数据库里,要在不停机的情况下全部迁成密文。我们选双读兼容加分批迁移。拦截器里内置的读取逻辑是:读出数据,判断是否有加密前缀,有前缀就解密返回,无前缀直接返回明文,兼容存量。
迁移脚本的核心逻辑让 Cursor 生成,但迁移策略自己设计:
迁移脚本要求: 1. 按数据库逐个跑,互不干扰 2. 每次迁移 1000 条,间隔 500ms 控制数据库压力 3. 每批迁移完做校验,解密后和原文比对 4. 支持断点续跑 5. 出错自动暂停,等人工介入先在测试库完整跑一遍,验证数据一致性,解密校验零错误,再上生产。生产库按数据库分批,选在凌晨流量低谷执行,全程监控。早上六点,所有数据库迁移完成。
7. 第七天:回归、日志审查与整改报告
最后一天三件事。全量回归测试,所有涉及敏感字段的接口全部过一遍。日志审查,确认没有任何接口还在日志里打印明文敏感数据。整改报告,把加密方案、改动范围、测试结果整理成文档提交合规团队。复测当天审计方抽查数据库,所有敏感字段密文存储,查询正常,接口响应正常。
逐系统验证清单可以按这个表逐项打勾,留下可审计记录:
| 检查项 | 验证方式 | 通过标准 |
|---|---|---|
| 字段已加密 | 直连数据库查该字段 | 值为密文且带前缀 |
| 读取正常 | 调接口查该字段 | 返回明文,格式正确 |
| 查询正常 | 用该字段做条件查询 | 能命中,结果正确 |
| 日志无明文 | 搜日志文件 | 无明文敏感值 |
| 接口无透传 | 抓包看返回值 | 按需脱敏或加密 |
| 存量已迁移 | 抽样比对 | 解密后与原文一致 |
8. 本篇常见错排查
报错一:Cursor 里模型调用返回 404。检查 baseUrl 是否写成https://taotoken.net/api/v1,末尾的/v1不能少。如果还是 404,去 https://taotoken.net/api-keys 确认 Key 是否有效、额度是否充足。
报错二:拦截器不生效,写进去还是明文。先确认拦截器是否注册成 Spring Bean,MyBatis 的Interceptor需要显式注册。再确认实体类字段上的注解是否被正确扫描,注解的@Retention必须是RUNTIME。
报错三:查询条件用密文字段查不到。这是类型 C 的典型问题。用于查询的字段必须用确定性加密,且查询时把明文转成确定性密文再拼进 SQL。如果用了随机 IV,同样的明文每次密文不同,永远查不到。
报错四:迁移后部分数据解密失败。大概率是密钥轮换没配好。检查解密时是否同时尝试新旧两个密钥,以及迁移脚本用的密钥和拦截器解密用的密钥是否一致。
报错五:分页查询结果里敏感字段没解密。MyBatis-Plus 的分页结果是包装对象,拦截器要单独处理IPage类型,不能只处理 List 和单对象。
9. 把架子搭对,再让 AI 加速
复盘下来,AI 提速的不是思考,是执行。加密工具类怎么设计密钥轮换、拦截器要不要缓存反射结果、迁移用双读还是停写、系统之间改造顺序怎么排,这些判断是我们自己做的。AI 做的是把这些判断落成代码的速度快了三四倍,同时帮我们减少了遗漏——那 5 处手拼 SQL、日志里的明文、接口透传的字段,散在各个角落,人工找成本极高。
如果你也面对等保三级整改,两条建议:先摸清楚要改哪些再动手,用 AI 批量扫比人工快三倍以上;把加解密收拢到一处,不要分散在业务代码里。架子搭对了,后面的事 AI 能帮你快三四倍;架子搭错了,AI 只会帮你更快走向错误方向。
接入和排障相关的操作,可以对照 https://taotoken.net/doc 里的接口说明;需要长期跑编码和 Agent 任务的团队,可以看 https://taotoken.net/coding-plan 的额度方案;日常验证模型对 Java、MyBatis 问题的回答质量,直接在 https://taotoken.net/model-chat 里试几轮就行。