Laravel中用户、团队、项目的角色与权限查询问题
Laravel 用户/团队/项目 三级权限配置问题
背景说明
已配置User、Team、Project、Role、Permission的关联关系:
- 用户可加入多个团队,一个团队包含多个项目
- 用户拥有用户级、团队级、项目级三类角色
- 用户在单个项目/团队中仅能拥有1个角色,自身角色仅为Admin或User
数据表结构
User表
- id
- name
- current_project_id
- current_team_id
- role_id
- 其他字段
Teams表
- id
- name
Projects表
- id
- name
- description
Roles表
- id
- name
- type(user,project,team)
Permissions表
- id
- name
role_permission表(角色-权限关联)
- permission_id
- role_id
project_user表(用户-项目-角色关联)
- project_id
- user_id
- role_id
team_user表(用户-团队-角色关联)
- team_id
- user_id
- role_id
现有代码实现
User模型
class User extends Authenticatable { public function projectRole() { // 问题:boot阶段current_project_id为空,无法筛选项目角色 return $this->belongsToMany(Role::class, 'project_user')->where('project_id', '=', $this->current_project_id)->first(); } public function projectPermissions() { return $this->projectRole()->permissions; } public function permissions() { // 问题:无法整合三类权限 } public function role() : BelongsTo { // 用户自身角色:Admin/User/Support return $this->belongsTo(Role::class); } public function userPermissions() { return $this->role->permissions; } }
Role模型
class Role extends Model { public function permissions() { return $this->belongsToMany(Permission::class); } }
Gate权限校验(待完善)
Gate::before(function ($user, $permission) { return $user->projectPermissions($user->currentProject)->contains($permission) || $user->teamPermissions($user->currentTeam)->contains($permission) || $user->teamPermissions($user->currentTeam)->contains($permission) || $user->userPermissions()->contains($permission); });
当前问题
projectRole方法在模型boot阶段current_project_id为空,无法正确筛选项目角色- 缺少整合用户/团队/项目三类权限的方法
- Gate校验逻辑不完善,且需要将权限传递至Inertia前端
解决方案
1. 修复项目/团队角色获取逻辑
将projectRole、teamRole改为支持动态传参的方法,避免boot阶段字段未加载的问题:
class User extends Authenticatable { // 获取指定项目的用户角色(默认用当前项目ID) public function projectRole($projectId = null) { $projectId = $projectId ?? $this->current_project_id; if (!$projectId) { return null; } return $this->belongsToMany(Role::class, 'project_user') ->wherePivot('project_id', $projectId) ->first(); } // 获取指定项目的用户权限 public function projectPermissions($projectId = null) { $projectRole = $this->projectRole($projectId); return $projectRole ? $projectRole->permissions : collect(); } // 新增:获取指定团队的用户角色 public function teamRole($teamId = null) { $teamId = $teamId ?? $this->current_team_id; if (!$teamId) { return null; } return $this->belongsToMany(Role::class, 'team_user') ->wherePivot('team_id', $teamId) ->first(); } // 新增:获取指定团队的用户权限 public function teamPermissions($teamId = null) { $teamRole = $this->teamRole($teamId); return $teamRole ? $teamRole->permissions : collect(); } // ... 原有role()、userPermissions()方法保持不变 }
2. 整合三类权限的方法
新增getAllPermissions方法,合并用户自身、当前团队、当前项目的权限并去重:
class User extends Authenticatable { // ... 其他方法 // 获取用户所有有效权限(用户级+团队级+项目级) public function getAllPermissions() { $permissions = collect(); // 合并用户自身权限 if ($this->role) { $permissions = $permissions->merge($this->role->permissions); } // 合并当前团队权限(如果存在当前团队) if ($this->current_team_id) { $teamRole = $this->teamRole(); if ($teamRole) { $permissions = $permissions->merge($teamRole->permissions); } } // 合并当前项目权限(如果存在当前项目) if ($this->current_project_id) { $projectRole = $this->projectRole(); if ($projectRole) { $permissions = $permissions->merge($projectRole->permissions); } } // 去重并返回 return $permissions->unique('id'); } }
3. 完善Gate校验与前端权限传递
修正Gate校验逻辑
优化权限判断顺序,增加超级管理员放行规则,处理空值场景:
// 在AppServiceProvider的boot方法中修改 Gate::before(function ($user, $permission) { // 超级管理员直接拥有所有权限 if ($user->role?->name === 'Admin') { return true; } $hasPermission = false; // 优先检查用户自身权限 if ($user->role) { $hasPermission = $user->role->permissions->contains('name', $permission); } // 自身无权限时,检查当前团队权限 if (!$hasPermission && $user->current_team_id) { $teamRole = $user->teamRole(); $hasPermission = $teamRole?->permissions->contains('name', $permission); } // 团队无权限时,检查当前项目权限 if (!$hasPermission && $user->current_project_id) { $projectRole = $user->projectRole(); $hasPermission = $projectRole?->permissions->contains('name', $permission); } return $hasPermission; });
将权限传递至Inertia前端
在HandleInertiaRequests中间件中共享用户权限列表:
// app/Http/Middleware/HandleInertiaRequests.php public function share(Request $request) { return array_merge(parent::share($request), [ 'auth' => [ 'user' => fn () => $request->user() ? [ 'id' => $request->user()->id, 'name' => $request->user()->name, // 将权限名称数组传递到前端 'permissions' => $request->user()->getAllPermissions()->pluck('name')->toArray(), ] : null, ], ]); }
前端可通过$page.props.auth.user.permissions获取权限列表,用于页面按钮/路由的权限控制。
内容的提问来源于stack exchange,提问作者Carlos Valdes Web
相关产品推荐
相关产品推荐

