跳转至

Laravel后台管理系统-Filament(权限管理)

环境

  • Php8.2
  • Mysql8.0

第一步:创建 Laravel 12 项目

  • 使用composer 安装示例项目 example-1
composer create-project laravel/laravel example-1
  • 配置数据库 .env
......
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=laravel_example_1
DB_USERNAME=root
DB_PASSWORD=123456
......
  • 迁移数据库
php artisan migrate
  • 运行项目
php artisan serve

第二步 安装Filament

  • 基础安装
composer require filament/filament:"^3.3" -W

php artisan filament:install --panels
  • 创建 admin_users 表 并创建守卫 admin
php artisan make:model AdminUser -m
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    /**
     * Run the migrations.
     */
    public function up(): void
    {
        Schema::create('admin_users', function (Blueprint $table) {
            $table->id();
            $table->string('name');
            $table->string('email')->unique();
            $table->timestamp('email_verified_at')->nullable();
            $table->string('password');
            $table->rememberToken();
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     */
    public function down(): void
    {
        Schema::dropIfExists('admin_users');
    }
};
php artisan migrate

更改auth.php

......
'guards' => [
        'web' => [
            'driver' => 'session',
            'provider' => 'users',
        ],
        'admin' => [
            'driver' => 'session',
            'provider' => 'admin_users',
        ],
    ],
    'providers' => [
        'users' => [
            'driver' => 'eloquent',
            'model' => env('AUTH_MODEL', App\Models\User::class),
        ],

        'admin_users' => [
            'driver' => 'eloquent',
            'model' => env('ADMIN_AUTH_MODEL', App\Models\AdminUser::class),
        ],
    ],
......

filament添加守卫admin

App\Providers\Filament\AdminPanelProvider.php

<?php

namespace App\Providers\Filament;

use Filament\Http\Middleware\Authenticate;
use Filament\Http\Middleware\AuthenticateSession;
use Filament\Http\Middleware\DisableBladeIconComponents;
use Filament\Http\Middleware\DispatchServingFilamentEvent;
use Filament\Pages;
use Filament\Panel;
use Filament\PanelProvider;
use Filament\Support\Colors\Color;
use Filament\Widgets;
use Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse;
use Illuminate\Cookie\Middleware\EncryptCookies;
use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken;
use Illuminate\Routing\Middleware\SubstituteBindings;
use Illuminate\Session\Middleware\StartSession;
use Illuminate\View\Middleware\ShareErrorsFromSession;

class AdminPanelProvider extends PanelProvider
{
    public function panel(Panel $panel): Panel
    {
        return $panel
            ->default()
            ->id('admin')
            ->path('admin')
            ->login()
            ->authGuard('admin')
            ->colors([
                'primary' => Color::Amber,
            ])
            ->discoverResources(in: app_path('Filament/Resources'), for: 'App\\Filament\\Resources')
            ->discoverPages(in: app_path('Filament/Pages'), for: 'App\\Filament\\Pages')
            ->pages([
                Pages\Dashboard::class,
            ])
            ->discoverWidgets(in: app_path('Filament/Widgets'), for: 'App\\Filament\\Widgets')
            ->widgets([
                Widgets\AccountWidget::class,
                Widgets\FilamentInfoWidget::class,
            ])
            ->middleware([
                EncryptCookies::class,
                AddQueuedCookiesToResponse::class,
                StartSession::class,
                AuthenticateSession::class,
                ShareErrorsFromSession::class,
                VerifyCsrfToken::class,
                SubstituteBindings::class,
                DisableBladeIconComponents::class,
                DispatchServingFilamentEvent::class,
            ])
            ->authMiddleware([
                Authenticate::class,
            ]);
    }
}

更改App\Models\AdminUser.php

<?php

namespace App\Models;

// use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;

class AdminUser extends Authenticatable
{
    /** @use HasFactory<\Database\Factories\UserFactory> */
    use HasFactory, Notifiable;

    /**
     * The attributes that are mass assignable.
     *
     * @var list<string>
     */
    protected $fillable = [
        'name',
        'email',
        'password',
    ];

    /**
     * The attributes that should be hidden for serialization.
     *
     * @var list<string>
     */
    protected $hidden = [
        'password',
        'remember_token',
    ];

    /**
     * Get the attributes that should be cast.
     *
     * @return array<string, string>
     */
    protected function casts(): array
    {
        return [
            'email_verified_at' => 'datetime',
            'password' => 'hashed',
        ];
    }
}
  • 创建user
php artisan make:filament-user

第三步 安装并配置filament-shield

  • 安装
  composer require bezhansalleh/filament-shield
  • 配置

php artisan vendor:publish --tag="filament-shield-config"
# config/filament-shield.php
<?php

return [
    ...
    // 定义认证服务Model,改为上一章创建的AdminUser
    'auth_provider_model' => [
        'fqcn' => 'App\\Models\\AdminUser',
    ],
    ...
];
  • 为AdminUser模型添加HasRoles Trait
use Spatie\Permission\Traits\HasRoles;

class AdminUser extends Authenticatable implements FilamentUser
{
    use HasRoles;
}

设置Shield

php artisan shield:setup
php artisan shield:install admin
php artisan shield:super-admin

发布翻译

# .env
APP_LOCALE=zh_CN
php artisan vendor:publish --tag="filament-shield-translations"
# lang/vendor/filament-shield/zh_CN/filament-shield.php
# 自定义翻译
'nav.group' => '权限管理',
  • 自定义策略

php artisan make:policy AdminUserPolicy --model=AdminUser
# app/Policies/AdminUserPolicy.php
# 不需要的权限可以自行删除
<?php

namespace App\Policies;

use App\Models\AdminUser;
use Illuminate\Auth\Access\HandlesAuthorization;

class AdminUserPolicy
{
    use HandlesAuthorization;
    /**
     * Determine whether the user can view any models.
     */
    public function viewAny(AdminUser $adminUser): bool
    {
        return $adminUser->canAny('view_admin::user');
    }

    /**
     * Determine whether the user can view the model.
     */
    public function view(AdminUser $adminUser): bool
    {
        return $adminUser->can('view_admin::user');
    }

    /**
     * Determine whether the user can create models.
     */
    public function create(AdminUser $adminUser): bool
    {
        return $adminUser->can('create_admin::user');
    }

    /**
     * Determine whether the user can update the model.
     */
    public function update(AdminUser $adminUser): bool
    {
        return $adminUser->can('update_admin::user');
    }

    /**
     * Determine whether the user can delete the model.
     */
    public function delete(AdminUser $adminUser): bool
    {
        return $adminUser->can('delete_admin::user');
    }

    /**
     * Determine whether the user can restore the model.
     */
    public function restore(AdminUser $adminUser): bool
    {
        return $adminUser->can('restore_admin::user');
    }

    /**
     * Determine whether the user can permanently delete the model.
     */
    public function forceDelete(AdminUser $adminUser): bool
    {
        return $adminUser->can('force_delete_admin::user');
    }
}
php artisan make:filament-resource AdminUser --generate
# app/Filament/Resources/AdminUserResource.php
<?php

namespace App\Filament\Resources;

use App\Filament\Resources\AdminUserResource\Pages;
use App\Models\AdminUser;
use Filament\Forms;
use Filament\Forms\Form;
use Filament\Resources\Resource;
use Filament\Tables;
use Filament\Tables\Table;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\SoftDeletingScope;
use Illuminate\Support\Facades\Auth;

class AdminUserResource extends Resource
{
    protected static ?string $model = AdminUser::class;

    protected static ?string $navigationIcon = 'heroicon-o-rectangle-stack';

    protected static ?string $navigationGroup = '权限管理';

    protected static ?string $navigationLabel = '用户管理';

    protected static ?string $modelLabel = '用户';

    public static function form(Form $form): Form
    {
        return $form
            ->schema([
                Forms\Components\TextInput::make('name')
                    ->label('用户名')
                    ->required()
                    ->maxLength(255),
                Forms\Components\TextInput::make('email')
                    ->label('邮箱')
                    ->email()
                    ->required()
                    ->maxLength(255),
                Forms\Components\TextInput::make('password')
                    ->label('密码')
                    ->password()
                    ->required()
                    ->maxLength(255),
                Forms\Components\Select::make('role')
                    ->relationship('roles', 'name')
                    ->label('角色')
                    ->required(),
            ]);
    }

    public static function table(Table $table): Table
    {
        return $table
            ->columns([
                Tables\Columns\TextColumn::make('name')
                    ->label('用户名')
                    ->searchable(),
                Tables\Columns\TextColumn::make('email')
                    ->label('邮箱')
                    ->searchable(),
                Tables\Columns\TextColumn::make('created_at')
                    ->label('创建时间')
                    ->dateTime(),
            ])
            ->filters([
                //
            ])
            ->actions([
                Tables\Actions\EditAction::make(),
            ])
            ->bulkActions([
                Tables\Actions\BulkActionGroup::make([
                    Tables\Actions\DeleteBulkAction::make(),
                ]),
            ]);
    }

    public static function getRelations(): array
    {
        return [
            //
        ];
    }

    public static function getPages(): array
    {
        return [
            'index' => Pages\ListAdminUsers::route('/'),
            'create' => Pages\CreateAdminUser::route('/create'),
            'edit' => Pages\EditAdminUser::route('/{record}/edit'),
        ];
    }
}

第四步 创建角色与用户

角色:超级管理员 root 普通管理员 user1 只读管理员 user2

php artisan make:model Category -m
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    /**
     * Run the migrations.
     */
    public function up(): void
    {
        Schema::create('categories', function (Blueprint $table) {
            $table->id();
            $table->string('catename');
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     */
    public function down(): void
    {
        Schema::dropIfExists('categories');
    }
};
php artisan make:migrate
php artisan make:policy CategoryPolicy --model=Category
<?php

namespace App\Policies;

use App\Models\AdminUser;
use Illuminate\Auth\Access\HandlesAuthorization;

class CategoryPolicy
{
    use HandlesAuthorization;

    /**
     * Determine whether the user can view any models (影响 Filament 菜单是否显示).
     */
    public function viewAny(AdminUser $user): bool
    {
        return $user->can('view_category');
    }

    /**
     * Determine whether the user can view the model.
     */
    public function view(AdminUser $user): bool
    {
        return $user->can('view_category');
    }

    /**
     * Determine whether the user can create models.
     */
    public function create(AdminUser $user): bool
    {
        return $user->can('create_category');
    }

    /**
     * Determine whether the user can update the model.
     */
    public function update(AdminUser $user): bool
    {
        return $user->can('update_category');
    }

    /**
     * Determine whether the user can delete the model.
     */
    public function delete(AdminUser $user): bool
    {
        return $user->can('delete_category');
    }

    public function deleteAny(AdminUser $user): bool
    {
        return $user->can('delete_any_category');
    }

    /**
     * Determine whether the user can restore the model.
     */
    public function restore(AdminUser $user): bool
    {
        return $user->can('restore_category');
    }

    /**
     * Determine whether the user can permanently delete the model.
     */
    public function forceDelete(AdminUser $user): bool
    {
        return $user->can('force_delete_category');
    }
}
php artisan make:filament-resource Category --generate