真正可靠的方式是在全局中间件中捕获401/403响应码并统一处理,结合$request->attr('auth_reason')区分失败原因,标准化返回JSON错误格式,避免依赖Auth::check()或钩子等不可靠方式。

ThinkPHP6 的授权异常处理不能只靠 Auth 类的失败回调或钩子,真正可靠的方式是捕获响应状态码,在全局中间件中统一拦截 401 和 403 响应并做日志、跳转或 JSON 返回等处理。
在全局中间件中捕获授权失败响应
授权逻辑执行后,框架最终会返回 401(未认证)或 403(已认证但无权限)。这些状态码是唯一可信的失败信号,必须在响应发出前捕获:
- 新建中间件(如 app/middleware/AuthExceptionMiddleware.php),注册为全局中间件
- 在
$next($request)执行后调用$response->getCode()判断是否为 401 或 403 - 若匹配,可记录日志、修改响应内容(如返回标准 JSON 格式),或重定向到登录页
- 注意:不要在中间件里调用
Auth::check()或依赖其返回值,它不等于最终响应状态
区分 401 与 403 并补充失败原因
两类失败需差异化处理。仅靠响应码不够,建议在鉴权中间件中提前设置上下文信息:
- 在 Token 验证失败时,用
$request->attr('auth_reason', 'token_expired')记录原因 - 在权限校验失败时,设为
'no_permission'或'route_forbidden' - 后续在异常中间件中通过
$request->attr('auth_reason')获取该值,用于日志归类或前端提示
统一返回标准 JSON 错误格式(API 场景)
面向前后端分离项目,推荐将 401/403 响应标准化,避免前端反复判断 status 和 data 结构:
立即学习“PHP免费学习笔记(深入)”;
- 若响应码为 401,构造:
json(['code' => 401, 'msg' => '登录已过期,请重新登录']) - 若为 403,构造:
json(['code' => 403, 'msg' => '暂无操作权限']) - 可进一步根据
$request->attr('auth_reason')动态调整 msg 内容,提升调试效率 - 确保不覆盖原本的响应头(如 Content-Type),必要时调用
$response->withHeader()
避免常见错误写法
以下方式看似简单,实则不可靠,容易漏掉关键失败路径:
- 在登录中间件里写
if (!Auth::check()) { return json(401); }—— 漏掉 JWT 签名错误、白名单绕过等场景 - 把日志逻辑放在自定义 Guard 类中 —— 多请求并发下
$this->lastRequest会被覆盖 - 监听
auth_fail钩子却未注册行为类,或钩子名拼错(如写成auth_failed) - 在控制器方法开头手动调
Auth::check()并 return —— 违背中间件职责分离原则,后期难以维护



















