异常审计日志需在失败瞬间完整记录“谁、对什么、做了什么、为何失败”:统一AOP切面处理成功与异常,结构化日志含操作快照、敏感脱敏、异步可靠落库及加密隔离。

异常发生时记录完整的业务操作审计日志,关键不是“捕获后打日志”,而是让异常本身成为审计上下文的一部分——即在操作失败的瞬间,仍能准确还原“谁、对什么、做了什么、为何失败”。这要求日志结构完整、上下文不丢失、敏感信息受控,且不因异常而中断记录流程。
确保失败操作也能生成标准 LogEntry
很多系统只在成功路径记录审计日志,导致异常场景下审计链断裂。正确做法是:无论方法成功或抛出异常,AOP切面都必须触发日志构造逻辑。
- 使用 @AfterReturning 捕获成功结果,用 @AfterThrowing 捕获异常(推荐指定 throwing = "ex" 参数)
- 两个通知共用同一套日志组装逻辑:从 JoinPoint 获取方法签名与参数,从 SecurityContext 提取 operatorId/operatorName,从 RequestContextHolder 提取 IP 和 User-Agent
- 失败日志中 status 设为 FAILED,errorMsg 字段只存精简摘要(如 "Email validation failed: invalid format"),避免堆栈全量写入(防日志膨胀和敏感泄露)
保留操作前后的关键业务快照
仅记录“失败”不够,需说明“改了什么才失败”。例如用户修改邮箱失败,应记录原值与提交的新值。
分析 Java Spring Boot 后端 Controller 代码,自动生成 Markdown 格式的接口文档。当用户说“生成 Java 接口文档”、“生成 API 文档”、“从 Spring 代码生成文档”时触发。
- 在 @AuditLog 注解中支持扩展属性,如 preSnapshotKey = "user",指示 AOP 从方法参数中提取原始对象
- 利用 Jackson 或 Gson 序列化该对象为 JSON 字符串,存入 extData 的 "before" 字段;若方法有返回值或异常,也序列化入 "after" 或 "errorDetail"
- 敏感字段(如手机号、身份证号)在序列化前自动脱敏(如 138****1234),不依赖业务层处理
异步落库必须保证异常日志不丢失
同步写库遇数据库故障会导致日志静默丢弃,尤其异常场景下更不可接受。
- 采用带持久化能力的内存队列(如 Disruptor + RingBuffer),或退而求其次使用 BlockingQueue + 定时刷盘补偿机制
- 若使用 @Async,需配置自定义 TaskExecutor 并设置拒绝策略为 CALLER_RUNS,防止线程池满时日志被丢弃
- 对 FAILED 日志增加重试标记和最大重试次数(如 retryCount=3),首次落库失败后写入本地磁盘临时文件,由后台任务兜底重发
加密存储与访问隔离
异常日志可能包含调试信息、参数详情甚至部分堆栈线索,必须防止未授权访问。
- extData 全字段 AES-256-GCM 加密,密钥由 KMS 托管,应用仅持密钥 ID
- 数据库表按租户或操作类型分库分表,FAILED 日志单独建表(如 audit_log_failed_202610),权限粒度控制到 DB 行级
- 查询接口对 errorMsg、extData 等字段默认返回脱敏后摘要,需二次鉴权(如 ROLE_AUDIT_ADMIN)才可查看原始内容
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










