Sprache:

Suche

Implementierung von Rollen und Berechtigungen in Laravel

  • Teilen:
Implementierung von Rollen und Berechtigungen in Laravel
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.

Usama Muneer

Usama Muneer

A web enthusiastic, self-motivated & detail-oriented professional Full-Stack Web Developer from Karachi, Pakistan with experience in developing applications using JavaScript, WordPress & Laravel specifically. Loves to write on different web technologies with an equally useful skill to make some sense out of it.