Laravel访问控制层与中间件:角色权限分配实现方案及示例
Hey there! Let me walk you through a proper, Laravel-compliant approach to build a role-based permission system with admin, employee, and supervisor roles (each with specific CRUD permissions) and assign those roles to users. I'll break this down into actionable steps with code examples you can adapt directly.
First, we'll set up the necessary database tables to handle roles, permissions, and their many-to-many relationships with users.
Create the roles table migration:
php artisan make:migration create_roles_table
Update the migration file:
public function up() { Schema::create('roles', function (Blueprint $table) { $table->id(); $table->string('name')->unique(); // e.g., 'admin', 'employee', 'supervisor' $table->string('display_name')->nullable(); // Human-readable name like 'Administrator' $table->text('description')->nullable(); $table->timestamps(); }); }
Next, create the permissions table:
php artisan make:migration create_permissions_table
Migration code:
public function up() { Schema::create('permissions', function (Blueprint $table) { $table->id(); $table->string('name')->unique(); // e.g., 'read', 'write', 'update', 'delete' $table->string('display_name')->nullable(); $table->text('description')->nullable(); $table->timestamps(); }); }
Now add pivot tables for the many-to-many relationships:
First, for roles ↔ permissions:
php artisan make:migration create_role_permission_table
Migration code:
public function up() { Schema::create('role_permission', function (Blueprint $table) { $table->foreignId('role_id')->constrained()->onDelete('cascade'); $table->foreignId('permission_id')->constrained()->onDelete('cascade'); $table->primary(['role_id', 'permission_id']); }); }
Then, for users ↔ roles:
php artisan make:migration create_user_role_table
Migration code:
public function up() { Schema::create('user_role', function (Blueprint $table) { $table->foreignId('user_id')->constrained()->onDelete('cascade'); $table->foreignId('role_id')->constrained()->onDelete('cascade'); $table->primary(['user_id', 'role_id']); }); }
Run all migrations:
php artisan migrate
Define the relationships in your models to link users, roles, and permissions.
Update the User model:
namespace App\Models; use Illuminate\Foundation\Auth\User as Authenticatable; use Illuminate\Database\Eloquent\Relations\BelongsToMany; class User extends Authenticatable { // ... existing code public function roles(): BelongsToMany { return $this->belongsToMany(Role::class); } // Check if user has a specific role public function hasRole(string $roleName): bool { return $this->roles()->where('name', $roleName)->exists(); } // Check if user has a specific permission public function hasPermission(string $permissionName): bool { return $this->roles()->whereHas('permissions', function ($query) use ($permissionName) { $query->where('name', $permissionName); })->exists(); } }
Create the Role model:
php artisan make:model Role
Update the Role model:
namespace App\Models; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsToMany; class Role extends Model { protected $fillable = ['name', 'display_name', 'description']; public function users(): BelongsToMany { return $this->belongsToMany(User::class); } public function permissions(): BelongsToMany { return $this->belongsToMany(Permission::class); } // Helper to assign permissions to the role public function assignPermission(Permission $permission): void { $this->permissions()->syncWithoutDetaching([$permission->id]); } }
Create the Permission model:
php artisan make:model Permission
Update the Permission model:
namespace App\Models; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsToMany; class Permission extends Model { protected $fillable = ['name', 'display_name', 'description']; public function roles(): BelongsToMany { return $this->belongsToMany(Role::class); } }
Create a seeder to populate your system with the default roles and permission assignments:
php artisan make:seeder RolePermissionSeeder
Update the seeder file:
namespace Database\Seeders; use App\Models\Role; use App\Models\Permission; use Illuminate\Database\Seeder; class RolePermissionSeeder extends Seeder { public function run() { // Create CRUD permissions $permissions = [ ['name' => 'read', 'display_name' => 'Read Access'], ['name' => 'write', 'display_name' => 'Write Access'], ['name' => 'update', 'display_name' => 'Update Access'], ['name' => 'delete', 'display_name' => 'Delete Access'], ]; foreach ($permissions as $perm) { Permission::firstOrCreate($perm); } // Create roles $adminRole = Role::firstOrCreate([ 'name' => 'admin', 'display_name' => 'Administrator', 'description' => 'Full system access' ]); $supervisorRole = Role::firstOrCreate([ 'name' => 'supervisor', 'display_name' => 'Supervisor', 'description' => 'Manage and edit content' ]); $employeeRole = Role::firstOrCreate([ 'name' => 'employee', 'display_name' => 'Employee', 'description' => 'Read-only access' ]); // Assign permissions to roles // Admin gets all permissions $allPermissions = Permission::all(); $adminRole->permissions()->sync($allPermissions->pluck('id')); // Supervisor gets read, write, update $supervisorPermissions = Permission::whereIn('name', ['read', 'write', 'update'])->get(); $supervisorRole->permissions()->sync($supervisorPermissions->pluck('id')); // Employee gets only read $employeePermission = Permission::where('name', 'read')->first(); $employeeRole->permissions()->sync([$employeePermission->id]); } }
Run the seeder:
php artisan db:seed --class=RolePermissionSeeder
You can assign roles to users in controllers, seeders, or even admin panels. Here's a quick example:
// Get a user and target role $user = User::find(1); $adminRole = Role::where('name', 'admin')->first(); // Assign the role (without removing existing roles) $user->roles()->syncWithoutDetaching([$adminRole->id]); // Or replace all current roles with new ones $user->roles()->sync([$adminRole->id, $supervisorRole->id]);
Use Laravel's Gates to enforce permission checks across your application.
Add this to app/Providers/AuthServiceProvider.php:
use App\Models\User; use Illuminate\Support\Facades\Gate; public function boot() { $this->registerPolicies(); // Define gates for each permission Gate::define('read', function (User $user) { return $user->hasPermission('read'); }); Gate::define('write', function (User $user) { return $user->hasPermission('write'); }); Gate::define('update', function (User $user) { return $user->hasPermission('update'); }); Gate::define('delete', function (User $user) { return $user->hasPermission('delete'); }); }
Now you can use these checks in controllers:
public function create() { // Ensure user has write permission $this->authorize('write'); return view('posts.create'); }
Or in Blade views:
@can('delete') <button type="button" class="btn btn-danger">Delete Post</button> @endcan @cannot('write') <p>You don't have permission to create new posts.</p> @endcannot
Restrict entire routes to specific roles with a custom middleware:
Create the middleware:
php artisan make:middleware CheckRole
Update the middleware file:
namespace App\Http\Middleware; use Closure; use Illuminate\Http\Request; use Symfony\Component\HttpFoundation\Response; class CheckRole { public function handle(Request $request, Closure $next, string $role): Response { if (!$request->user()->hasRole($role)) { abort(403, 'Unauthorized action.'); } return $next($request); } }
Register it in app/Http/Kernel.php under $routeMiddleware:
'role' => \App\Http\Middleware\CheckRole::class,
Use it in routes:
Route::get('/admin/dashboard', [AdminController::class, 'dashboard'])->middleware('role:admin');
That's a solid, Laravel-standard implementation you can extend as your app grows!
内容的提问来源于stack exchange,提问作者Madhu Nair

