#08 FilamentPHP基礎
認証とアクセス制御——ポリシーとロールで管理する
Filamentのアクセス制御の仕組み
Filamentのアクセス制御は2層構造になっています。
1. パネルへのアクセス → User::canAccessPanel()
2. リソース操作の制御 → Resource::canXxx() または Laravel Policy
まずパネルに入れるかを判定し、次にリソースの各操作(閲覧・作成・編集・削除)ごとに許可・拒否を判断します。
パネルへのアクセス制限
FilamentUser インターフェースで制御
<?php
// 📁 app/Models/User.php
namespace App\Models;
use Filament\Models\Contracts\FilamentUser;
use Filament\Panel;
use Illuminate\Foundation\Auth\User as Authenticatable;
class User extends Authenticatable implements FilamentUser
{
public function canAccessPanel(Panel $panel): bool
{
// 方法1: 管理者メールドメインのみ許可
return str_ends_with($this->email, '@mycompany.com');
}
}
// 方法2: is_admin フラグで制御
public function canAccessPanel(Panel $panel): bool
{
return (bool) $this->is_admin;
}
// 方法3: ロールで制御(spatie/laravel-permission などと組み合わせ)
public function canAccessPanel(Panel $panel): bool
{
return $this->hasRole(['admin', 'editor', 'viewer']);
}
// 方法4: 複数パネルを運用する場合はパネルIDで分岐
public function canAccessPanel(Panel $panel): bool
{
return match ($panel->getId()) {
'admin' => $this->hasRole('admin'),
'staff' => $this->hasRole(['admin', 'staff']),
default => false,
};
}
PanelProvider でミドルウェアを追加
// 📁 app/Providers/Filament/AdminPanelProvider.php
->authMiddleware([
Authenticate::class,
// カスタムミドルウェアを追加
\App\Http\Middleware\EnsureEmailIsVerified::class,
])
リソース単位のアクセス制御
Resource クラスに直接定義する方法
<?php
// 📁 app/Filament/Resources/PostResource.php
class PostResource extends Resource
{
// リソース全体を非表示にする(サイドバーから消す)
public static function canViewAny(): bool
{
return auth()->user()?->hasPermissionTo('view posts') ?? false;
}
// 新規作成を禁止
public static function canCreate(): bool
{
return auth()->user()?->hasPermissionTo('create posts') ?? false;
}
// 特定レコードの編集を制御
public static function canEdit(\Illuminate\Database\Eloquent\Model $record): bool
{
$user = auth()->user();
// 管理者か、自分の投稿なら編集可能
return $user?->hasRole('admin') || $record->user_id === $user?->id;
}
// 特定レコードの削除を制御
public static function canDelete(\Illuminate\Database\Eloquent\Model $record): bool
{
return auth()->user()?->hasRole('admin') ?? false;
}
// 詳細表示の制御
public static function canView(\Illuminate\Database\Eloquent\Model $record): bool
{
return true; // 全員に許可
}
}
Laravel Policy との連携
Laravelのポリシーを作成して、Filamentに自動適用させる方法です。
ポリシーを生成
php artisan make:policy PostPolicy --model=Post
<?php
// 📁 app/Policies/PostPolicy.php
namespace App\Policies;
use App\Models\Post;
use App\Models\User;
use Illuminate\Auth\Access\HandlesAuthorization;
class PostPolicy
{
use HandlesAuthorization;
// リスト表示(管理画面の一覧)
public function viewAny(User $user): bool
{
return $user->hasPermissionTo('view any posts');
}
// 個別表示
public function view(User $user, Post $post): bool
{
return $user->hasRole('admin') || $post->user_id === $user->id;
}
// 作成
public function create(User $user): bool
{
return $user->hasPermissionTo('create posts');
}
// 更新
public function update(User $user, Post $post): bool
{
// 管理者か、下書きの自分の投稿のみ編集可
return $user->hasRole('admin') ||
($post->user_id === $user->id && ! $post->is_published);
}
// 削除
public function delete(User $user, Post $post): bool
{
return $user->hasRole('admin');
}
// ソフトデリートの復元
public function restore(User $user, Post $post): bool
{
return $user->hasRole('admin');
}
// 完全削除
public function forceDelete(User $user, Post $post): bool
{
return $user->hasRole('admin');
}
}
ポリシーをAuthServiceProviderに登録
Laravel 11以降は AppServiceProvider で登録します。
<?php
// 📁 app/Providers/AppServiceProvider.php
use Illuminate\Support\Facades\Gate;
public function boot(): void
{
Gate::policy(\App\Models\Post::class, \App\Policies\PostPolicy::class);
}
Filamentでポリシーを自動適用する
PostResource に $model が設定されている場合、Filamentは自動でポリシーを検出します。ただし canCreate() などを上書きしていない場合に限ります。
明示的にポリシーを使う設定:
// 📁 app/Providers/Filament/AdminPanelProvider.php
->authorizationGuard('web') // 使用するガードを指定
ロールベースのアクセス制御
spatie/laravel-permission を使ったロールベース制御の実装例です。
composer require spatie/laravel-permission
php artisan vendor:publish --provider="Spatie\Permission\PermissionServiceProvider"
php artisan migrate
ロールとパーミッションのシード
<?php
// 📁 database/seeders/RoleSeeder.php
use Spatie\Permission\Models\Permission;
use Spatie\Permission\Models\Role;
class RoleSeeder extends Seeder
{
public function run(): void
{
// パーミッションを作成
$permissions = [
'view posts', 'create posts', 'edit posts', 'delete posts',
'view users', 'create users', 'edit users', 'delete users',
];
foreach ($permissions as $permission) {
Permission::firstOrCreate(['name' => $permission]);
}
// ロールを作成してパーミッションを割り当て
$admin = Role::firstOrCreate(['name' => 'admin']);
$admin->syncPermissions($permissions); // 全権限
$editor = Role::firstOrCreate(['name' => 'editor']);
$editor->syncPermissions([
'view posts', 'create posts', 'edit posts',
]);
$viewer = Role::firstOrCreate(['name' => 'viewer']);
$viewer->syncPermissions(['view posts']);
}
}
リソースにパーミッションチェックを組み込む
class PostResource extends Resource
{
public static function canViewAny(): bool
{
return auth()->user()?->hasPermissionTo('view posts') ?? false;
}
public static function canCreate(): bool
{
return auth()->user()?->hasPermissionTo('create posts') ?? false;
}
public static function canEdit(\Illuminate\Database\Eloquent\Model $record): bool
{
return auth()->user()?->hasPermissionTo('edit posts') ?? false;
}
public static function canDelete(\Illuminate\Database\Eloquent\Model $record): bool
{
return auth()->user()?->hasPermissionTo('delete posts') ?? false;
}
}
NavigationGroup のアクセス制御
ナビゲーショングループごとに表示を制御します。
// 📁 app/Filament/Resources/UserResource.php
protected static ?string $navigationGroup = 'ユーザー管理';
// この NavigationGroup が表示されるかどうか
public static function canViewAny(): bool
{
return auth()->user()?->hasRole('admin') ?? false;
}
PanelProvider でナビゲーショングループを明示的に定義
// 📁 app/Providers/Filament/AdminPanelProvider.php
use Filament\Navigation\NavigationGroup;
->navigationGroups([
NavigationGroup::make()
->label('コンテンツ管理')
->icon('heroicon-o-document-text'),
NavigationGroup::make()
->label('ユーザー管理')
->icon('heroicon-o-users')
->collapsed(), // デフォルトで折りたたむ
])
パネル全体への認証ミドルウェア設定
// 📁 app/Providers/Filament/AdminPanelProvider.php
->authMiddleware([
// ログインしていない場合はログインページへリダイレクト
\Filament\Http\Middleware\Authenticate::class,
])
// ログインページのルートをカスタマイズ
->login(\App\Filament\Pages\Auth\Login::class)
// メール確認を強制
->requiresEmailVerification()
カスタムログインページの例
<?php
// 📁 app/Filament/Pages/Auth/Login.php
namespace App\Filament\Pages\Auth;
use Filament\Pages\Auth\Login as BaseLogin;
class Login extends BaseLogin
{
// ログインフォームにIP制限を追加する例
public function authenticate(): ?LoginResponse
{
$allowedIps = config('admin.allowed_ips', []);
if (! empty($allowedIps) && ! in_array(request()->ip(), $allowedIps)) {
$this->addError('form', 'このIPアドレスからはアクセスできません。');
return null;
}
return parent::authenticate();
}
}
まとめ
canAccessPanel(Panel $panel)でユーザーごとにパネルへのアクセスを制御- リソースには
canViewAny()/canCreate()/canEdit($record)/canDelete($record)を定義 - Laravel の Policy を作成・登録するとFilamentが自動で適用する
spatie/laravel-permissionでロール・パーミッション管理を実装できるNavigationGroupにもアクセス制御を設定可能->authMiddleware([])でパネル全体に認証ミドルウェアを適用
次回はグローバル検索とナビゲーションのカスタマイズを学びます。