中间件中应通过Auth::id()或$request->attr('user')['id']获取用户ID,操作行为需按控制器/方法打标或查配置表动态判断,审计日志须记录脱敏参数、关键数据快照、session_id等并写入数据库。

中间件里怎么拿到用户ID和操作行为
ThinkPHP 的中间件默认拿不到登录态,因为 Auth 或 Session 可能还没初始化,或者当前请求是 API 场景(JWT、token),得自己捞。别直接用 session('user_id'),它在 CLI 或无 session 环境会报错或返回空。
推荐做法是统一从请求上下文提取:
- 如果是基于
think-auth或自定义登录,用Auth::id()(需确保 Auth 已注册并完成初始化) - 如果是 token 验证,中间件中应已解析出用户信息(比如你在前置中间件里把
$user写进了$request->withAttr('user', $user)),那就取$request->attr('user')['id'] - 兜底方案:用
Cookie或 Header(如X-User-ID)手动读取,但必须校验合法性,不能直接信任
哪些操作算“敏感”?怎么动态判断
硬编码判断 'delete'、'admin/user/del' 这类字符串容易漏、难维护。更靠谱的是按控制器/方法打标 + 白名单驱动。
实操建议:
立即学习“PHP免费学习笔记(深入)”;
- 在控制器方法上加注解,比如
@Sensitive("删除用户"),然后中间件里用反射读$reflect->getDocComment()提取 - 或者建一张配置表
audit_rules,字段含rule_path(如admin.user/delete)、level(1=普通,2=高危)、desc,运行时查库匹配$request->rule()->getRuleName() - 注意:别对所有 POST/PUT/DELETE 全盘记录——像上传头像、提交搜索表单这类操作噪音太大,反而掩盖真实风险
日志内容写什么?为什么不能只记 URL 和 IP
只记 $request->url() 和 $request->ip() 在审计回溯时基本没用。攻击者改个参数就能绕过,而你连他删了哪个用户都不知道。
必须捕获的关键字段:
-
$request->param()—— 但要过滤敏感键(如password、token),可用白名单:array_intersect_key($params, array_flip(['id', 'status', 'role_id'])) - 操作前后的关键数据快照(如删除前查一次
User::find($id),记录用户名、邮箱;仅对高危规则启用,避免性能拖垮) -
$request->method()+$request->isAjax()+$request->header('user-agent'),辅助识别自动化工具调用 - 特别注意:记录
session_id()而非仅 IP,同一 IP 下多个账号共用时才分得清人
审计日志写到哪?数据库 vs 文件的取舍
别用 Log::write() 往文件写审计日志——查起来慢、没法关联用户、权限难控制、还可能被误删。直接写数据库最稳妥,哪怕只是单表 audit_logs。
建表要点:
- 字段至少含:
user_id、ip、session_id、rule_name、params(JSON 格式存)、before_data(TEXT,可为空)、created_at - 给
user_id和created_at加联合索引,查某人某天的操作才不卡 - 如果并发高,用
Db::table('audit_logs')->insertGetId()同步写;若怕阻塞主流程,可推到队列(但得接受几秒延迟,且队列失败时要有降级落盘机制)
真正麻烦的是日志脱敏策略和保留周期——这得结合公司安全规范来定,不是代码能自动解决的。



















