必须用中间件记后台操作日志,因其唯一稳定位于路由解析完成、控制器执行前,可获取完整请求上下文;控制器和auth钩子因时机不当易漏数据、难审计。

真正能落地的后台操作日志,必须在中间件里做——控制器手动记、基类统一写、Auth钩子捕获,全都不稳定、易漏、难审计。
为什么必须用中间件,而不是控制器或 Auth 钩子
中间件是唯一能稳定卡在「路由解析完成、控制器执行前」的位置。这时 $request->action()、$request->controller()、Auth::id() 全部可用;而控制器里写日志,异常分支没覆盖、参数没抓全、登录态可能已失效;Auth 钩子(如 onAuthCheck)触发太早,拿不到 object_id、真实 IP,权限拒绝时流程直接中断,根本没机会记。
常见错误现象:
-
Auth::id()在基类initialize()里返回null—— 因为它执行时机早于权限中间件 - 登录接口被记日志,但
session尚未写入,Auth::id()拿不到值 - API 请求带 JWT,但钩子只认 session,认证方式不兼容
AdminLogMiddleware 怎么写才不丢上下文
核心是两件事:前置获取关键字段 + 后置判断结果。不要在 handle() 开头就记录,得等 $next($request) 执行完再收尾。
实操要点:
- 用
$request->url(true)获取完整 URL(含 query),别用$request->url(),否则重写后路径失真 - 用户 ID 必须走
think\Auth::id()或封装好的getLoginAdminId(),避免直读session('admin_id')(CLI、JWT、多守卫场景会崩) - 敏感参数过滤必须显式:先
$param = $request->param(),再unset($param['password'], $param['token'], $param['old_password']) - 状态判断不能依赖
$response->getCode()—— 500 异常未被捕获时它也返回 200。正确做法是捕获ValidateException、DbException,再结合响应体里的code字段(如$response->getData()['code'] ?? 0)
日志渠道和数据库表结构怎么配才可查可用
默认的 single 渠道和 file 驱动完全不适合审计:查不了、索不了、格式混乱。必须单独建 admin 渠道,并指向数据库。
配置与建表关键点:
- 日志写入必须显式调用
Log::channel('admin')->info(),否则全进默认渠道,配置再好也白搭 - 数据库驱动配置中,
log_table的created_at字段设为CURRENT_TIMESTAMP,禁用 ThinkPHP 自动时间戳填充,避免和主表冲突 - 表结构必须拆字段:至少含
admin_id(非空或 NULL)、module、controller、action、ip、method、url、input(JSON 类型或 TEXT)、result_code('success'/'fail',别用 HTTP 状态码) - 高频查询字段(如
admin_id、created_at、action)必须加联合索引,否则日志表撑不过一周
免日志接口和性能隔离怎么处理才不出错
登录、登出、验证码、健康检查这些接口必须跳过日志,但不能靠 URL 字符串匹配(/admin/login 和 /api/login 容易漏判),也不能靠路由分组(分组可能嵌套、动态注册)。
安全且稳定的方案:
- 用
in_array($request->action(), ['login', 'logout', 'captcha', 'ping'])过滤,$request->action()是方法名,稳定可靠 - 日志写入必须脱离主事务:用
Db::connect('log_db')->table('admin_log')->insert(...)单独建连接,别和业务共用一个 DB 实例 - 别用 MyISAM 表存日志——它不支持事务,主操作回滚时日志却已落盘,导致审计数据和业务状态不一致
- 生产环境禁用同步写库,优先走
Log::channel('admin')+ 数据库驱动异步落盘;若自研落库,务必加flock()或改用 Redis 队列缓冲
最常被忽略的是:日志字段设计不预留 object_id 和 rule_name,结果半年后想查“谁删了 ID=88 的用户”,发现日志里只有 user/delete 四个字,连对象都对不上。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











