敏感操作审计日志必须结构化捕获操作人、动作语义、脱敏参数和数据快照并独立存储;中间件中优先用auth::id()获取用户id,jwt场景取$request->attr('user')['id'],兜底校验x-user-id;动作用$request->rule()->getrulename()获取,避免url解析;敏感性通过audit_rules表动态匹配,高危操作启用before_data快照;参数仅保留业务关键字段,日志写入须脱离主事务并建联合索引。

敏感操作审计日志不能只记 URL 和 IP,否则查不出“谁删了哪个用户”,也分不清是误操作还是恶意行为。必须结构化捕获操作人、动作语义、脱敏参数和数据快照,并写入独立可查的存储介质。
中间件里怎么安全拿到 user_id 和 action
中间件默认拿不到登录态,Auth::id() 可能为 null,session('user_id') 在 CLI 或无 session 场景会出错。别硬写 session 或直接读 cookie。
- 优先用
Auth::id(),但要确保 Auth 服务已注册且完成初始化(常见于登录后) - 若走 JWT/token 验证,应在前置中间件中把用户信息写入请求属性,例如
$request->withAttr('user', $user),然后取$request->attr('user')['id'] - 兜底方案:读
X-User-IDHeader,但必须校验签名或关联 session token,不可直接信任 -
$request->rule()->getRuleName()比解析 URL 更稳,返回如admin/user/delete;$request->action()返回方法名,配合控制器名拼成admin/User/delete也可用
哪些操作该记、怎么动态判断敏感性
硬编码 'delete' 或 '/admin/user/del' 易漏、难维护,且无法区分“删除测试账号”和“删除管理员”。白名单 + 规则表才是可持续方案。
- 建配置表
audit_rules,字段含rule_path(如admin.user/delete)、level(1=普通,2=高危)、desc(如“删除用户主数据”) - 中间件中用
Db::name('audit_rules')->where('rule_path', $request->rule()->getRuleName())->find()实时匹配 - 对高危操作(
level = 2),额外启用数据快照:比如UserModel::find($params['id'])查出用户名、邮箱、状态,存入before_data字段 - 跳过登录、登出、验证码等免审接口,用
in_array($request->action(), ['login', 'logout', 'captcha'])过滤,别靠 URL 匹配
日志内容写什么、怎么避免性能拖垮
记录全部 $request->param() 是最常见错误——体积大、含敏感字段、模糊了业务意图。数据库写入本身也不能卡住主流程。
- 参数只保留关键业务字段:
array_intersect_key($params, array_flip(['id', 'status', 'role_id', 'amount'])),显式剔除password、token、card_no - 高危操作才查快照,且只查必要字段(如
username、email、mobile),不用toArray()全量序列化 - 日志必须写数据库,别用
Log::write()写文件——查起来慢、权限难控、没法关联用户;但写库要脱离当前事务:Db::connect('log_db')->name('audit_log')->insert($log) -
created_at和admin_id必须建联合索引,否则查“张三昨天改过哪些配置”会全表扫描
Db::listen() 怎么补上模型事件漏掉的操作
模型的 afterUpdate 钩子根本捕获不到 Db::table('user')->where('id', 1)->update([...]) 或存储过程调用,Db::listen() 是唯一覆盖所有 SQL 变更的手段。
- 在
app/common.php中注册:Db::listen(function ($sql, $bind, $time, $type) { ... })(ThinkPHP 6.x 签名) - 用
$type判断是否为INSERT/UPDATE/DELETE,跳过SELECT和健康检查查询 - 从请求上下文取
Auth::id()和Request::instance()->ip();若失败,设user_id = null并标记is_anonymous = 1 - UPDATE/DELETE 时,解析
$bind提取主键值,再查旧数据生成before_data;别用serialize(),用json_encode($data, JSON_UNESCAPED_UNICODE)
真正落地的审计不是堆字段,而是让每条日志都能回答三个问题:谁干的?干了什么?影响了什么数据?中间件定入口、规则表管范围、Db::listen() 补盲区、独立连接保性能——缺一不可。最容易被忽略的是快照时机和事务隔离,写日志前没查旧值,或日志和业务共用一个数据库连接,出事时就只剩“操作执行了,但不知道改成了啥”。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











