审计SDK概述:功能、架构与核心组件
讲多租户权限隔离之前,咱们得先把审计SDK这个底子摸清楚。不是光看接口文档就够的,你得理解它为什么这么设计,每个模块解决了什么实际问题。
我记得刚接手公司审计系统的时候,第一反应就是——这玩意儿不就是记个日志吗?后来踩了不少坑才明白,审计SDK的复杂度远比你想象的高。特别是在多租户场景下,它要解决的问题已经不是“记了什么”,而是“谁在什么环境下对哪个租户的资源做了什么,这个操作是否合规”。
一、审计SDK的核心架构
我习惯把审计SDK分成三层来看:接入层、处理层、存储层。这三层各司其职,又互相配合。
各层之间通过标准化接口通信。你想想看,如果接入层直接往数据库写审计数据,后续想切换存储引擎或升级脱敏策略,那改动量就太大了。分层的好处就是——我只需要改中间层逻辑,上层和下层基本不用动。
二、审计SDK的五大功能模块
功能设计上,我总结了五个核心模块,按调用链顺序排下来:
| 模块 | 职责 | 多租户场景要点 |
|---|---|---|
| 接入拦截 | 自动捕获业务请求中的审计事件 | 根据租户元数据判定事件归属域 |
| 上下文构建 | 组装完整的操作上下文(时间、来源、操作人、租户ID、资源ID) | 租户ID必须强制非空校验 |
| 内容脱敏 | 对敏感字段(手机号、身份证、等)进行默认或自定义脱敏 | 不同的租户可能有不同的脱敏规则 |
| 异步缓冲 | 将审计记录写入内存缓冲队列,批量或定时刷入存储 | 缓冲队列按租户打标,避免全量冲刷 |
| 持久化与归档 | 写入数据库、ES、日志文件,支持冷热数据分层 | 表或索引必须包含租户隔离字段 |
说白了,这些模块不是哪个团队拍脑袋想出来的,而是被无数线上故障逼出来的。我遇到过几次因为没有脱敏导致敏感信息泄漏的 case,从那以后我就在团队里硬性规定——接入审计SDK的第一步,先把脱敏模块配好。
三、核心组件逐一拆解
下面这几个组件,是你在改造时绕不开的。我给你挨个儿说说,顺便把坑也指出来。
3.1 AuditContext(审计上下文)
审计上下文是整个SDK的数据载体。它负责收集一次操作中的所有元信息。我记得早期版本里,大家随便往里面塞字段,后来发现字段多了反而不好查。所以我现在的做法是:只保留必选字段和扩展字段两个区。
扩展字段: 业务自定义的KV对,需要序列化前脱敏
3.2 AuditInterceptor(审计拦截器)
拦截器是接入层的主心骨。它可以绑定在Spring的Interceptor、Filter或者AOP切面上。本质上就一个职责——在请求进入业务逻辑之前和返回之后,分别触发审计事件。
3.3 TenantAwareSerializer(租户感知序列化器)
我的做法是——在序列化器里注册一个脱敏策略工厂,策略按照租户ID + 字段名称双键定位。这样每个租户可以独立配置自己的脱敏规则。
3.4 AuditBuffer(审计缓冲队列)
缓冲队列是性能的守护者。正常情况下,审计记录先进内存队列,达到阈值或超时间隔后批量写入存储。这里有一个关键设计——按租户拆分缓冲通道。
为什么要这么做?我给你举个真实案例:一次线上故障导致某个大租户突然爆发数万倍的审计流量,如果所有租户都混用一个队列,其他租户的审计记录会被拖死。分开队列后,即便大租户积压,小租户的审计也能正常写入。
3.5 AuditStorageManager(审计存储管理器)
存储管理器负责最终落地。它要处理的事情包括:
- 根据租户ID分配对应的表或索引
- 处理写失败时的重试补偿机制
- 管理冷热数据的生命周期
- 提供审计记录的可追溯接口
我习惯在存储管理器里做一层“写前校验”——比如检查tenantId是否有值、时间戳是否合法。宁可丢一条格式不对的记录,也不能把脏数据写入存储。
改造多租户时,每个组件都得带上租户意识。
四、总结一下架构特点
审计SDK并不是越复杂越好。我见过一些团队把SDK做得非常“灵活”,结果反而没人敢用。我的建议是——先做标准化,再留扩展点。
| 维度 | 建议做法 |
|---|---|
| 接入方式 | 统一用注解 + 配置开关,业务方零代码接入 |
| 数据字段 | 必填字段严格校验,扩展字段零校验 |
| 脱敏规则 | 内置默认脱敏函数,允许租户级注册自定义函数 |
| 存储策略 | 默认写入数据库,支持通过SPI扩展ES、ClickHouse |
| 故障隔离 | 缓冲队列和写入线程池都按租户隔离 |
嗯,审计SDK的概述就先说这么多。这些内容看起来是基础,但你把这些模块和组件在脑海里拼起来之后,后面我们做改造和权限隔离的时候,你就知道应该在哪些地方动刀了。