checkauth函数必须定义在application/common.php中,名称固定,需显式引入auth和config类,调用auth::setuser((int)session('uid'))后执行auth::check(),节点名须与auth_rule.name完全一致且小写,_initialize中须用error或redirect终止流程。

自定义函数名必须叫 checkAuth 才能统一拦截
TP5 权限校验不是靠中间件自动触发的,得靠每个控制器里手动调用。但重复写 $auth->check() 太容易漏、难维护,所以必须封装成一个全局可用的函数。名字不能随便起——所有控制器里都用 if(!checkAuth()),那这个函数就必须叫 checkAuth,且要确保它在任何控制器里都能直接调用。
常见错误是把函数定义在某个控制器内部,或者起名成 authCheckverifyPermission 之类,结果别处调用时报 Call to undefined function。
- 函数必须定义在
application/common.php(或extend/下可自动加载的位置) - 不能依赖
$this,因为它是普通函数,不是类方法 - 必须显式引入
use util\Auth;和use think\facade\Config; - 超级管理员 ID 必须从配置读取:
config('auth_superadmin'),别硬编码'37'
checkAuth 函数里必须调用 Auth::setUser($uid)
这是最常被跳过的一步:不调用 Auth::setUser($uid),Auth::check() 永远返回 false。原因很直接——TP5 的 Auth 类默认只查 session 缓存里的权限列表,而登录后没人主动把用户权限塞进去,缓存就是空的。
你看到按钮隐藏了、菜单没显示,但 POST 请求照样能打进来,就是因为这行漏了。
- 调用位置:必须在
checkAuth()开头,拿到session('uid')后立刻执行 - 参数必须是整型 UID:
Auth::setUser((int)session('uid')),字符串 ID 会失效 - 不要在中间件或登录逻辑里“顺手”调一次就以为够了——每次权限检查前都得再调一遍,因为缓存可能过期或被清空
权限节点名必须和路由规则名完全一致
很多人拼出 admin/user/delete 却发现校验失败,其实是节点名和数据库 auth_rule.name 字段对不上。TP5 的 Auth::check() 只认 name 字段的原始值,不解析 URL、不匹配通配符、不自动转小写。
典型翻车点:request()->module().'/'.request()->controller().'/'.request()->action() 拼出来是 admin/User/delete(首字母大写),但数据库里存的是 admin/user/delete,直接不匹配。
- 务必统一用
strtolower()处理:$rule = strtolower($m.'/'. $c .'/'. $a) - 如果用了路由绑定(如
Route::post('admin/user/:id', 'UserController@delete')),节点名建议固定为admin/user/delete,而不是带:id的动态形式 - 千万别用
$request->url()或$request->path()拼节点——它们返回的是真实访问路径,和权限系统无关
别在 _initialize 里用 return 中断流程
TP5 的控制器 _initialize() 方法不支持 return 提前退出。你写 if(!checkAuth()){ return error('无权访问'); },PHP 不报错,但后续代码照常执行,权限形同虚设。
真正有效的做法只有两个:抛异常或重定向。
- 推荐用
$this->error()或$this->redirect(),它们会终止当前请求流 - 如果用
error()辅助函数,确保它已定义且会 exit,否则只是输出字符串 - 不要试图在
_initialize里返回布尔值、赋值给变量、或靠 if-else 控制后续逻辑——TP5 不吃这套
Auth::setUser() 调用时机、_initialize 的终止方式,三者只要一个没对齐,整个权限链就断了。线上最容易被忽略的是缓存未填充和节点大小写不一致,一查日志全是 check() false,但根本看不出哪步挂了。











