You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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);
});

当前问题

  1. projectRole方法在模型boot阶段current_project_id为空,无法正确筛选项目角色
  2. 缺少整合用户/团队/项目三类权限的方法
  3. 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 09:53:15