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

Filamentphp Shield多面板权限显示异常及修改后无权限问题

集中式角色权限管理问题及修复方案

问题概述

  • 基于Filament Shield插件实现角色权限管理,系统包含admin、home、app三个面板
  • 角色列表页面可展示所有面板的114项权限,但在admin面板编辑super_admin角色时,仅能看到admin面板的54项权限,无法管理其他面板的权限
  • 尝试修改代码实现集中管理后,出现所有权限均无法显示的问题,修改后的代码如下:
public static function form(Form $form): Form
{
    return $form
        ->schema([
            Forms\Components\Grid::make()
                ->schema([
                    Forms\Components\Section::make()
                        ->schema([
                            Forms\Components\TextInput::make('name')
                                ->label(__('filament-shield::filament-shield.field.name'))
                                ->unique(
                                    ignoreRecord: true,
                                    modifyRuleUsing: fn (Unique $rule) => ! Utils::isTenancyEnabled() ? $rule : $rule->where(Utils::getTenantModelForeignKey(), Filament::getTenant()?->id)
                                )
                                ->required()
                                ->maxLength(255),

                            Forms\Components\TextInput::make('guard_name')
                                ->label(__('filament-shield::filament-shield.field.guard_name'))
                                ->default(Utils::getFilamentAuthGuard())
                                ->nullable()
                                ->maxLength(255),

                            Forms\Components\Select::make(config('permission.column_names.team_foreign_key'))
                                ->label(__('filament-shield::filament-shield.field.team'))
                                ->placeholder(__('filament-shield::filament-shield.field.team.placeholder'))
                                ->default([Filament::getTenant()?->id])
                                ->options(fn (): Arrayable => Utils::getTenantModel() ? Utils::getTenantModel()::pluck('name', 'id') : collect())
                                ->hidden(fn (): bool => ! (static::shield()->isCentralApp() && Utils::isTenancyEnabled()))
                                ->dehydrated(fn (): bool => ! (static::shield()->isCentralApp() && Utils::isTenancyEnabled())),

                            ShieldSelectAllToggle::make('select_all')
                                ->onIcon('heroicon-s-shield-check')
                                ->offIcon('heroicon-s-shield-exclamation')
                                ->label(__('filament-shield::filament-shield.field.select_all.name'))
                                ->helperText(fn (): HtmlString => new HtmlString(__('filament-shield::filament-shield.field.select_all.message')))
                                ->dehydrated(fn (bool $state): bool => $state),
                        ])
                        ->columns([
                            'sm' => 2,
                            'lg' => 3,
                        ]),
                ]),

                Forms\Components\Section::make('Permissions')
                ->schema([
                    Tabs::make('Tabs')
                        ->tabs([
                            self::getPermissionsTab('admin'),
                            self::getPermissionsTab('home'),
                            self::getPermissionsTab('MOJO'),
                        ]),
                ]),

            ]);

}

protected static function getPermissionsTab(string $panel): Tab
{
    $originalPanel = Filament::getCurrentPanel();

    Filament::setCurrentPanel(Filament::getPanel($panel));

    $permissions = Permission::where('guard_name', $panel)->pluck('name', 'name')->toArray();

    Filament::setCurrentPanel($originalPanel);

    return Tab::make(Str::headline($panel))
        ->schema([
            CheckboxList::make('permissions')
                ->label('Permissions from ' . ucfirst($panel))
                ->options(array_combine($permissions, $permissions))
                ->columns(2),
        ]);
}

代码问题分析

  1. 面板标识不匹配:第三个Tab传入的面板参数是MOJO,但系统实际面板为app,导致查询不到对应guard_name的权限
  2. 字段名冲突:多个Tab的CheckboxList均使用permissions作为字段名,Filament无法区分不同面板的权限,导致数据绑定异常
  3. 权限查询逻辑不完善:直接查询Permission模型未利用Shield工具类,可能导致权限列表不完整;冗余的array_combine操作无意义
  4. 权限默认值缺失:未设置角色已有权限的默认选中状态,用户无法直观看到当前权限配置

修复方案

1. 修正面板标识

将第三个Tab的面板参数改为app,匹配系统实际面板:

Tabs::make('Tabs')
    ->tabs([
        self::getPermissionsTab('admin'),
        self::getPermissionsTab('home'),
        self::getPermissionsTab('app'), // 修正面板名称
    ]),

2. 区分权限字段名

为每个面板的权限字段添加前缀,避免冲突,并实现权限合并:
修改getPermissionsTab方法的CheckboxList字段:

CheckboxList::make("permissions_{$panel}") // 添加面板前缀区分字段
    ->label('Permissions from ' . ucfirst($panel))
    ->options($permissions)
    ->columns(2)
    ->default(function ($record) use ($panel) {
        // 默认选中角色已拥有的对应面板权限
        return $record?->permissions->where('guard_name', $panel)->pluck('name')->toArray() ?? [];
    }),

添加表单数据预处理方法,合并所有面板的权限:

protected function mutateFormDataBeforeSave(array $data): array
{
    $mergedPermissions = [];
    // 遍历所有面板,合并权限
    foreach (['admin', 'home', 'app'] as $panel) {
        if (isset($data["permissions_{$panel}"])) {
            $mergedPermissions = array_merge($mergedPermissions, $data["permissions_{$panel}"]);
            unset($data["permissions_{$panel}"]);
        }
    }
    $data['permissions'] = $mergedPermissions;
    return $data;
}

3. 优化权限查询逻辑

使用Shield的Utils类获取对应面板的完整权限列表:

use BezhanSalleh\FilamentShield\Utils;

// ...

protected static function getPermissionsTab(string $panel): Tab
{
    $originalPanel = Filament::getCurrentPanel();
    $targetPanel = Filament::getPanel($panel);
    
    Filament::setCurrentPanel($targetPanel);
    
    // 利用Shield工具类获取对应面板的所有权限
    $permissions = Utils::getAllPermissions()
        ->where('guard_name', $targetPanel->getAuthGuard())
        ->pluck('name', 'name')
        ->toArray();
    
    Filament::setCurrentPanel($originalPanel);
    
    return Tab::make(Str::headline($panel))
        ->schema([
            CheckboxList::make("permissions_{$panel}")
                ->label('Permissions from ' . ucfirst($panel))
                ->options($permissions)
                ->columns(2)
                ->default(function ($record) use ($targetPanel) {
                    return $record?->permissions->where('guard_name', $targetPanel->getAuthGuard())->pluck('name')->toArray() ?? [];
                }),
        ]);
}

4. 移除冗余操作

删除array_combine($permissions, $permissions),因为pluck('name','name')已返回键值一致的数组,直接使用$permissions即可。

内容的提问来源于stack exchange,提问作者KrutSednar

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.13 04:54:51