必须根据ThinkPHP版本适配权限方案:TP6需手动调用Auth::setUser($uid)并显式传app参数,TP8则依赖Route::option('permission')元数据注入,且auth_rule表中rule_code字段必填,缓存须按role_id粒度存储并主动清理。

要在ThinkPHP项目中稳定实现权限控制,必须清楚不同版本对Auth机制的处理差异——TP6默认不自动初始化权限缓存,TP8则强化了中间件与路由元数据的绑定能力,跳过版本适配直接写check()会导致90%的后台权限失效。
确认当前ThinkPHP版本并匹配权限方案
打开项目根目录下的composer.json,查找"topthink/thinkphp"字段的版本号:若为^6.0或^6.3,必须手动调用Auth::setUser($uid);若为^8.0,可启用Route::option('permission')元数据自动注入。
执行php think version命令,终端输出含v8.前缀即为TP8,此时auth_rule表中rule_code字段必须存在且非空——TP6允许为空,TP8校验该字段缺失时直接抛出InvalidArgumentException异常。
TP6权限初始化:登录后必须显式触发缓存加载
第一步:在登录成功后的控制器方法末尾,添加Auth::setUser($user['id']),不能只设session('user_id', $user['id'])。
立即学习“PHP免费学习笔记(深入)”;
第二步:检查中间件是否传入正确的app参数。多应用模式下(如admin/api分离),若未指定Auth::check('admin/user/list', $uid, 'admin'),权限判定将默认查app应用下的规则表,导致始终返回false。
第三步:确保auth_group_access表中用户ID与角色ID的关联记录真实存在,【TP6不校验角色状态,即使auth_group.status=0,只要关联存在就参与权限计算】。
TP8权限声明:用路由元数据替代字符串拼接
方法一:在路由定义时直接绑定权限码
Route::post('admin/article/delete', 'ArticleController@delete')->option(['permission' => 'article:delete']);
方法二:使用注解路由(需启用注解支持)
/** @permission("user:export") */
public function export() { ... }
方法三:全局映射规则(推荐用于旧项目迁移)
在app/middleware/AuthMiddleware.php中重写getPermissionCode()方法,将$request->rule()->getName()返回的admin.user.list自动转为user:list格式——【TP8默认不启用此映射,必须自行实现】。
权限缓存策略:按角色粒度存Redis而非用户ID
① 权限缓存key必须为role_permissions_{$roleId},不能用auth_{$userId}——TP6/TP8均不支持用户级缓存自动更新,角色变更后用户权限仍沿用旧缓存。
② 首次查询权限时,从auth_group_access → auth_group → auth_rule三表JOIN获取完整权限集,存为Redis Set结构。
③ 后端分配新权限时,执行Redis::del('role_permissions_'.$roleId),否则该角色所有用户下次请求仍读取旧缓存。
这一步操作起来很简单,直接把文件拖进去就行。



















