Aktualisiert für Laravel 6.0
Einführung
In diesem Artikel werden wir lernen, wie man Rollen und Berechtigungen in Laravel implementiert und einrichtet. Es gibt viele Pakete, die diese Dinge für Sie erledigen, indem Sie sie einfach über den Composer ziehen, sie einrichten und schon kann es losgehen. Aber ich habe das Gefühl, dass diese Pakete oft zu viel Inhalt haben, den ich nicht wirklich brauche.
Was ist, wenn man nur ein einfaches Rollen- und Rechte-Setup für sein Projekt braucht? Mit diesen Paketen hat man weniger Optionen, um sie an die eigenen Bedürfnisse anzupassen. Einige Leute bezeichnen dies oft als das Rad neu erfinden, aber in Wirklichkeit ist es das nicht. Das ist es nicht.
Wir werden versuchen, fast jede Kleinigkeit zum Einrichten von Rollen und Berechtigungen zu behandeln. Lassen Sie uns also direkt eintauchen und sehen, was wir in diesem Artikel behandeln.
- Datenbankstruktur und Migrationen
- Beziehungen zwischen Modellen
- Benutzerdefinierte Direktiven für Ansichten
- Zuweisung von Rollen und Berechtigungen an Benutzer
- Einrichten einer Middleware für Rollen und Berechtigungen
Es gibt so viel zu behandeln in diesem Artikel, lassen Sie uns mit einer frischen Laravel-Installation beginnen.
Einrichten
Öffnen Sie Ihr Terminal und erstellen Sie ein neues Laravel-Projekt, indem Sie den folgenden Befehl eingeben
$ laravel new roles-permissions
DYI-Konfiguration
Richten Sie Ihre Datenbank ein, konfigurieren Sie sie mit Ihrem Projekt und gehen Sie zum nächsten Schritt über.
Aufbau unseres Authentifizierungsgerüsts
Beginnen wir mit der Erstellung unseres Authentifizierungsgerüsts:
$ php artisan make:auth
Modelle und Migrationen
Erstellen Sie zunächst die erforderlichen Modelle und Migrationen für dieses Projekt.
Geben Sie in das Terminal ein:
$ php artisan make:model Permission -m $ php artisan make:model Role -m
Wie Sie vielleicht wissen, wird mit der Option -m eine Migrationsdatei für das Modell erstellt. Jetzt haben Sie zwei neue Migrationsdateien, die darauf warten, dass Sie neue Felder hinzufügen. Führen Sie den folgenden Befehl aus und migrieren Sie Ihre Datenbank.
$ php artisan migrate
Bearbeiten Sie die Migrationsdatei für Berechtigungen
Für die Berechtigungstabelle benötigen wir jetzt nur noch zwei Felder, eine ID, einen Slug und einen Namen. Fügen wir diese in unsere Migrationsdatei ein, so sollte unser Schema wie folgt aussehen
Schema::create('permissions', function (Blueprint $table) { $table->increments('id'); $table->string('slug'); //edit-posts $table->string('name'); // edit posts $table->timestamps(); });
Dasselbe gilt für die Migrationsdatei Role
Auch für die Tabelle "Rollen" werden die gleichen Felder verwendet.
Schema::create('roles', function (Blueprint $table) { $table->increments('id'); $table->string('slug'); //web-developer $table->string('name'); //Web Developer $table->timestamps(); });
Hinzufügen von Pivot-Tabellen
Nach unserem Verständnis benötigen wir die Pivot-Tabellen für das folgende Regelwerk.
- Benutzer kann Berechtigung haben
Für diese erste Pivot-Tabelle erstellen wir eine neue Migrationsdatei für die Tabelle users_permissions
$ php artisan make:migration create_users_permissions_table --create=users_permissions
Für diese Pivot-Tabelle zwischen Benutzern und Berechtigungen sollte unser Schema wie folgt aussehen
Schema::create('users_permissions', function (Blueprint $table) { $table->integer('user_id')->unsigned(); $table->integer('permission_id')->unsigned(); //FOREIGN KEY CONSTRAINTS $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade'); $table->foreign('permission_id')->references('id')->on('permissions')->onDelete('cascade'); //SETTING THE PRIMARY KEYS $table->primary(['user_id','permission_id']); });
Hier haben wir die Fremdschlüssel-Beschränkungen festgelegt, um die entsprechenden Datensätze zu löschen, wenn ein Benutzer oder eine Berechtigung gelöscht wird. Primärschlüssel für diese Tabelle sind user_id und permission_id.
- Benutzer kann Rolle haben
Lassen Sie uns nun eine Pivot-Tabelle für users_roles erstellen.
$ php artisan make:migration create_users_roles_table --create=users_roles
Die Felder in dieser Tabelle sind im Wesentlichen dieselben wie in der Tabelle users_permissions. Unser Schema für diese Tabelle wird wie folgt aussehen:
Schema::create('users_roles', function (Blueprint $table) { $table->integer('user_id')->unsigned(); $table->integer('role_id')->unsigned(); //FOREIGN KEY CONSTRAINTS $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade'); $table->foreign('role_id')->references('id')->on('roles')->onDelete('cascade'); //SETTING THE PRIMARY KEYS $table->primary(['user_id','role_id']); });
- Unter einer bestimmten Rolle kann ein Benutzer bestimmte Berechtigungen haben
Zum Beispiel kann ein Benutzer die Erlaubnis haben, ein Thema zu veröffentlichen, und ein Administrator kann die Erlaubnis haben, ein Thema zu bearbeiten oder zu löschen. In diesem Fall sollten wir eine neue Tabelle für roles_permissions einrichten, um diese Komplexität zu bewältigen.
$ php artisan make:migration create_roles_permissions_table --create=roles_permissions
Das Schema wird wie folgt aussehen:
Schema::create('roles_permissions', function (Blueprint $table) { $table->integer('role_id')->unsigned(); $table->integer('permission_id')->unsigned(); //FOREIGN KEY CONSTRAINTS $table->foreign('role_id')->references('id')->on('roles')->onDelete('cascade'); $table->foreign('permission_id')->references('id')->on('permissions')->onDelete('cascade'); //SETTING THE PRIMARY KEYS $table->primary(['role_id','permission_id']); });
Wenn das für Sie noch keinen Sinn ergibt, dann warten Sie, bis wir die Beziehungen zwischen unseren Tabellen aufgebaut haben. Dann wird alles klarer, das verspreche ich.
Lassen Sie uns jetzt alle Dinge migrieren.
$ php artisan migrate
Aufbau der Beziehungen
Wir beginnen mit der Erstellung der Beziehungen zwischen den Rollen und der Berechtigungstabelle. In unserer Role.php
//Role.php public function permissions() { return $this->belongsToMany(Permission::class,'roles_permissions'); }
Dasselbe gilt für Permission.php in umgekehrter Form.
//Permission.php public function roles() { return $this->belongsToMany(Role::class,'roles_permissions'); }
Nun zu den Benutzern. Ein Benutzer hat viele Rollen. Ein Benutzer kann viele Berechtigungen haben. Aber eine Rolle hat möglicherweise viele Benutzer und eine Berechtigung hat viele Benutzer. Wir müssen also in unserem Benutzermodell viele zu vielen Beziehungen einrichten. Aber wir werden einen neuen Trait für diese Logik erstellen, so dass wir, wenn wir in der Zukunft ein weiteres Modell in unserem Projekt hinzufügen, dieses einfach einbinden können.
Erstellen einer Eigenschaft
Innerhalb unseres App-Verzeichnisses erstellen wir ein neues Verzeichnis mit dem Namen Permissions und erstellen eine neue Datei namens HasPermissionsTrait.php
<?php namespace App\Permissions; use App\Permission; use App\Role; trait HasPermissionsTrait { public function roles() { return $this->belongsToMany(Role::class,'users_roles'); } public function permissions() { return $this->belongsToMany(Permission::class,'users_permissions'); } }
Es wurde ein netter kleiner Trait eingerichtet, um Benutzerbeziehungen zu verwalten. Zurück in unserem User-Modell importieren wir einfach diesen Trait und können loslegen.
Benutzer hasRole
Jetzt erstellen wir eine neue Funktion in unserer HasPermissionsTrait.php und nennen sie hasRole wie folgt
public function hasRole( ... $roles ) { foreach ($roles as $role) { if ($this->roles->contains('slug', $role)) { return true; } } return false; }
Hier durchlaufen wir die Rollen und prüfen anhand des Slug-Feldes, ob diese spezifische Rolle existiert. Sie können dies überprüfen oder debuggen, indem Sie:
$user = $request->user(); //getting the current logged in user dd($user->hasRole('admin','editor')); // and so on
Überprüfung der Berechtigungen
Jetzt müssen wir die Möglichkeit schaffen, einem Benutzer bestimmte Rechte zu erteilen. Aber halt, hier haben wir ein paar Bedingungen, mit denen wir umgehen müssen:
- Der Benutzer kann für einige Aktionen individuelle Berechtigungen haben.
Zurück in HasPermissionsTrait.php werden wir einige neue Methoden für Benutzerrechte hinzufügen:
protected function hasPermissionTo($permission) { return $this->hasPermission($permission); } protected function hasPermission($permission) { return (bool) $this->permissions->where('slug', $permission->slug)->count(); }
Wir werden die "can"-Direktive von Laravel verwenden, um zu prüfen, ob der Benutzer eine Berechtigung hat. Statt $user->hasPermissionTo() verwenden wir $user->can() Dazu müssen wir einen neuen PermissionsServiceProvider für die Autorisierung erstellen
$ php artisan make:provider PermissionsServiceProvider
Registrieren Sie Ihren Dienstanbieter und gehen Sie zur Boot-Methode, um uns ein Gateway zur Verwendung der can()-Methode zur Verfügung zu stellen.
//PermissionsServiceProvider.php public function boot() { Permission::get()->map(function($permission){ Gate::define($permission->slug, function($user) use ($permission){ return $user->hasPermissionTo($permission); }); }); }
Was wir hier tun, ist, alle Berechtigungen abzubilden, den Berechtigungs-Slug (in unserem Fall) zu definieren und schließlich zu prüfen, ob der Benutzer eine Berechtigung hat. Sie können mehr über die Gate-Fassade von Laravel in der Dokumentation von Laravel erfahren. Sie können es als ausprobieren:
dd($user->can('permission-slug'));
- Benutzer kann über eine Rolle eine Berechtigung haben
Um diese Bedingung zu erfüllen, wird in unserem HasPermissionsTrait.
//HasPermissionsTrait.php public function hasPermissionThroughRole($permission) { foreach ($permission->roles as $role){ if($this->roles->contains($role)) { return true; } } return false; }
Hier durchlaufen wir jede Berechtigung, die mit einer Rolle verbunden ist. Denken Sie daran, dass wir eine Beziehung von vielen zu vielen zwischen den Rollen und der Berechtigungstabelle eingerichtet haben.
Die Methode hasPermissionTo() prüft nun zwischen diesen beiden Bedingungen.
//HasPermissionsTrait.php public function hasPermissionTo($permission) { return $this->hasPermissionThroughRole($permission) || $this->hasPermission($permission); }
Erteilung von Genehmigungen
Angenommen, wir möchten einem angemeldeten Benutzer eine Reihe von Berechtigungen erteilen, dann können wir dies folgendermaßen erreichen
//HasPermissionsTrait.php public function givePermissionsTo(... $permissions) { $permissions = $this->getAllPermissions($permissions); dd($permissions); if($permissions === null) { return $this; } $this->permissions()->saveMany($permissions); return $this; }
Löschen von Berechtigungen
Zum Löschen oder Entfernen von Berechtigungen aus dem Benutzerbereich können wir die Methode detach verwenden.
//HasPermissionsTrait.php public function deletePermissions( ... $permissions ) { $permissions = $this->getAllPermissions($permissions); $this->permissions()->detach($permissions); return $this; }
Das ist ziemlich einfach, das Gleiche gilt auch für Rollen. Probiert es aus und hinterlasst uns einen Kommentar, wie ihr es gemacht habt.
Hinzufügen der Sämaschinen
In diesem Teil werden wir Seeder für Berechtigungen, Rollen und Benutzer erstellen und unseren Methodensatz entsprechend verifizieren. Beginnen wir also mit der Erstellung von Seedern.
$ php artisan make:seeder PermissionTableSeeder $ php artisan make:seeder RoleTableSeeder $ php artisan make:seeder UserTableSeeder
Beginnen wir mit der Bearbeitung und fügen ein paar Datensätze in UserTableSeeder.php hinzu
//UserTableSeeder.php $dev_role = Role::where('slug','developer')->first(); $manager_role = Role::where('slug', 'manager')->first(); $dev_perm = Permission::where('slug','create-tasks')->first(); $manager_perm = Permission::where('slug','edit-users')->first(); $developer = new User(); $developer->name = 'Usama Muneer'; $developer->email = '[email protected]'; $developer->password = bcrypt('secret'); $developer->save(); $developer->roles()->attach($dev_role); $developer->permissions()->attach($dev_perm); $manager = new User(); $manager->name = 'Asad Butt'; $manager->email = '[email protected]'; $manager->password = bcrypt('secret'); $manager->save(); $manager->roles()->attach($manager_role); $manager->permissions()->attach($manager_perm);
Hier weisen wir unseren neu angelegten Benutzern die Rollen und Berechtigungen zu.
Gehen Sie zur nächsten Datei, d.h. RoleTableSeeder.php
$dev_permission = Permission::where('slug','create-tasks')->first(); $manager_permission = Permission::where('slug', 'edit-users')->first(); //RoleTableSeeder.php $dev_role = new Role(); $dev_role->slug = 'developer'; $dev_role->name = 'Front-end Developer'; $dev_role->save(); $dev_role->permissions()->attach($dev_permission); $manager_role = new Role(); $manager_role->slug = 'manager'; $manager_role->name = 'Assistant Manager'; $manager_role->save(); $manager_role->permissions()->attach($manager_permission);
Hier binden wir die Berechtigungen an die Rollen an. Denken Sie daran, dass wir eine ManyToMany-Beziehung zwischen Rollen und Berechtigungen haben.
Die letzte Datei ist PermissionTableSeeder.php
//PermissionTableSeeder.php $dev_role = Role::where('slug','developer')->first(); $manager_role = Role::where('slug', 'manager')->first(); $createTasks = new Permission(); $createTasks->slug = 'create-tasks'; $createTasks->name = 'Create Tasks'; $createTasks->save(); $createTasks->roles()->attach($dev_role); $editUsers = new Permission(); $editUsers->slug = 'edit-users'; $editUsers->name = 'Edit Users'; $editUsers->save(); $editUsers->roles()->attach($manager_role);
Da nun alle unsere Seeder einsatzbereit sind, können wir unsere Migration mit dem --seed-Flag ausführen.
$ php artisan migrate:refresh --seed
Um dies in Ihren Routendateien zu testen, können wir sterben und aufgeben:
$user = $request->user();
dd($user->hasRole('developer')); //will return true, if user has role
dd($user->givePermissionsTo('create-tasks')); // will return permission, if not null
dd($user->can('create-tasks')); // will return true, if user has permission
Einrichten der benutzerdefinierten Blade-Direktiven
Wir können die can-Direktive in unseren Blade-Dateien verwenden, die eine von Laravels vordefinierten Methoden ist, die für die Autorisierung von Berechtigungen für Benutzer verwendet werden kann,
Hier werden wir eine Role-Direktive erstellen, die in unseren Blade-Dateien für die Überprüfung der Rollen für Benutzer zur Verfügung stehen soll.
Innerhalb der Boot-Methode in PermissionsServiceProvider.php
//PermissionsServiceProvider.php Blade::directive('role', function ($role){ return "<?php if(auth()->check() && auth()->user()->hasRole({$role})) :"; }); Blade::directive('endrole', function ($role){ return "<?php endif; ?>"; });
Innerhalb unserer View-Dateien können wir sie wie folgt verwenden:
@role('admin') <h1>Hello from the admin</h1> @endrole
Einrichten der Middleware
Um unsere Routen zu schützen, können wir die Middleware so einrichten, dass sie dies tut.
$ php artisan make:middleware RoleMiddleware
Fügen Sie die Middleware in Ihren Kernel ein und richten Sie die Handle-Methode wie folgt ein
public function handle($request, Closure $next, $role, $permission = null) { if(!$request->user()->hasRole($role)) { abort(404); } if($permission !== null && !$request->user()->can($permission)) { abort(404); } return $next($request); }
Direkt in unseren Routen können wir so vorgehen
Route::group(['middleware' => 'role:admin'], function() { Route::get('/admin', function() { return 'Welcome Admin'; }); });
Einfach genug?
Letzte Worte
Das Github Repo wurde für Laravel 6.0 aktualisiert.
Hier ist die funktionierende Github rep0 für diese Laravel-Installation. Klonen oder laden Sie die Dateien herunter, wenn Sie irgendwo dazwischen stecken bleiben. Außerdem können Sie diese kleinen Tricks selbst ausprobieren und Rollen und Berechtigungen für Ihre nächste Laravel-Anwendung einrichten. Viel Erfolg! Wenn Sie Fragen zu diesem Artikel haben, können Sie unten einen Kommentar hinterlassen oder Sie können uns auch fragen auf Twitter.