thinkphp rbac权限控制需手动建表、调用auth::setuser()、中间件映射路由名并缓存校验,任一环节缺失均导致auth::check()恒返回false;权限名与路由名须字面完全一致,不自动转换符号或大小写;多应用需加前缀;登录后须立即调用auth::setuser()写入session;redis缓存应基于角色而非用户,权限变更须主动清除对应key;接口层必须二次校验权限,不可仅依赖前端判断。

ThinkPHP 的 RBAC 权限控制不是开箱即用的安全机制,它必须靠你手动建表、显式调用 Auth::setUser()、并在中间件里做路由名映射和缓存校验——漏掉任一环,Auth::check() 就会永远返回 false。
权限名和路由名必须字面完全一致
权限是否生效,第一关就卡在 rule_name 和当前请求的路由名是否严格匹配。ThinkPHP 不会自动转换斜杠/点号、不忽略大小写、不截断前缀。
- 若路由定义是
Route::get('admin/user/list', 'admin.UserController@list'),权限名只能是admin/user/list,不能写成admin.user.list或/admin/user/list - 如果用了
->name('user.index')显式命名,那$request->rule()->getName()才返回user.index;否则它可能返回控制器反射路径(如\app\controller\Admin\UserController@list),这种值根本查不到权限 - 多应用下(如
admin和api),权限名必须带应用前缀,比如admin:user:list,否则不同应用的权限会互相污染
登录后必须立刻调用 Auth::setUser($uid)
这是权限静默失效的头号原因。TP6 的 Auth 类默认从 session 读 think_auth 缓存,但它不会自己写——你不调,缓存就是空的,后续所有 Auth::check() 都失败。
- 调用位置必须在登录成功、重定向之前。放在跳转后的页面逻辑里,中间件已经跑完了
- 确认 session 中确实写入了
think_auth键,其值应包含role_id和rules数组,而不是只有user_id - 如果用了 Redis 缓存权限列表,
Auth::setUser()不会自动同步到 Redis,得额外触发PermissionService::generateForUser($uid)这类逻辑
中间件里别查库,要用 Redis 缓存角色权限集
高并发下每次请求都 JOIN 四张表查权限,数据库立马扛不住。正确做法是把「角色 → 权限名列表」预存进 Redis,中间件只做 SISMEMBER 判断。
- 缓存 key 建议用
auth_role_rules_{$roleId},value 是SMEMBERS存储的字符串集合,如admin/user:list、admin/user:delete - 不要用
Cache::get('permission_list_'.$uid)存用户级权限——用户有多角色时,得合并多个角色的权限集,缓存 key 应基于角色而非用户 - 权限变更(增删改)或角色调整后,必须主动清除对应
auth_role_rules_*的 Redis key,否则新权限永不生效
菜单渲染和按钮显隐不能只靠前端判断
前端根据后端返回的 permission_name 白名单过滤菜单和按钮是对的,但接口层必须二次校验——否则绕过前端直接调 API 仍能越权操作。
- 后台菜单渲染时,从
sys_permission表查type = 'menu'且status = 1的记录,再用当前用户权限白名单过滤显示 - 按钮点击触发的接口,必须在对应控制器方法或中间件里再次调用
Auth::check('admin/user/delete'),不能只信前端传来的user_has_perm - 别把权限逻辑塞进模板里用
{if $user->can('user:delete')}这种方式判断——模板层无法做实时权限校验,且容易暴露逻辑
最易被忽略的是缓存失效时机:用户换角色、权限被禁用、管理员批量调整角色权限,这三类操作都必须触发对应 Redis key 清除,否则权限状态会长期滞后。而这个清除动作,往往被写在“权限管理”模块的某个角落,上线后没人记得补。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











