Laravel Authentication und Authorization Beispiel

 

Laravel ist für Benutzer-Anmeldungen bereits vorbereitet, das Aktivieren der Funktion erfolgt mit einem einfachen Befehl.

Vorbereitung Laravel/ui

Als Vorbereitung wird für die Bootstrap-Files laravel/ui benötigt:

composer require laravel/ui
php artisan ui bootstrap

Authentifizierung hinzufügen

Der Befehl: php artisan ui vue --auth (seit Laravel 6.0, für Laravel 5.x php artisan make:auth) installiert alle Voraussetzungen für eine Benutzeranmeldung

vagrant@homestead:~/Code/Laravel$ php artisan make:auth
Created View: /home/vagrant/Code/Laravel/resources/views/auth/login.blade.php
Created View: /home/vagrant/Code/Laravel/resources/views/auth/register.blade.php
Created View: /home/vagrant/Code/Laravel/resources/views/auth/passwords/email.blade.php
Created View: /home/vagrant/Code/Laravel/resources/views/auth/passwords/reset.blade.php
Created View: /home/vagrant/Code/Laravel/resources/views/auth/emails/password.blade.php
Created View: /home/vagrant/Code/Laravel/resources/views/layouts/app.blade.php
Created View: /home/vagrant/Code/Laravel/resources/views/home.blade.php
Created View: /home/vagrant/Code/Laravel/resources/views/welcome.blade.php
Installed HomeController.
Updated Routes File.
Authentication scaffolding generated successfully!
vagrant@homestead:~/Code/Laravel$

Die Javascript-Sourcen sollten mit "npm run dev" kompiliert werden.

 

Die Standard-Welcome-View,

wird mit mit der Application's Landing Page ersetzt:

Der Befehl erzeugt die notwendigen Views, einen "HomeController" und die Datei: routes\web.php wird um folgende Einträge erweitert:

Auth::routes();
Route::get('/home', 'HomeController@index');

Fortsetzung zum Myinput-Beispiel

Wer unserem Beispiel  Laravel 5 Beispiel - Schritt für Schritt gefolgt ist, bekommt die zuvor erstellte Datei /layouts/app.blade.php ersetzt,

unsere Demo-App /myinputs ist aber immer noch öffentlich, also ohne einer Benutzeranmeldung, verfügbar:

Controller nur für authentifizierte Benutzer

Der Controller "MyinputController" kann mit folgender Konstruktormethode nur für authentifizierte Benutzer erlaubt werden:

public function __construct()
{
$this->middleware('auth');
}

<?php

namespace App\Http\Controllers;
use App\Myinput;
use View;
use Illuminate\Http\Request;
use App\Http\Requests;


class MyinputController extends Controller
{

  public function __construct()
  {
      $this->middleware('auth');
  }

Beim Aufruf unseres Beispiels: http://TESTURL/myinputs werden wir automatisch zur Anmeldung umgeleitet.

Damit nach der Anmeldung nicht die Standard-Home-View kommt, können wir die Routes anpassen. Dadurch kommt anstelle des Home-Controllers unsere bereits erstellte Übersichtseite. Dazu ändere ich in der Datei routes/web.php folgende Zeile:

Route::get('/home', 'HomeController@index');

auf:

Route::get('/home', 'MyinputController@index');

Authorization

Des weiteren ist es möglich, dass nur bestimmte Benutzer, bestimmte Funktionen aufrufen können: Authorization.

Für die Demonstration dieser Funktion, erweitere ich die Users-Tabelle in der Datenbank mit der Spalte "role_id". Ziel ist es, dass nur bestimmte Benutzer die Rechte für das Erstellen und Bearbeiten der Einträge bekommen (Admin). Die Benutzertabelle wird dazu um einen Wert erweitert: role_id. 

Benutzer mit allen Rechten bekommen die: role_id=1, alle anderen: role_id=0. Das Beispiel dient dem Verständnis, für grössere Werbeprojekte empfehle ich das Paket laravel-permission.

Erweitern einer Datenbanktabelle

Eine Datenbank sollte im Nachhinein nicht direkt über PHP-Myadmin geändert werden, sondern mit einer Migration. Mit Hilfe der Migrationsdateien können Änderungen an der Applikation nachvollzogen und die Datenbank kann daraus jederzeit neu generiert werden.

Ich erzeuge also eine neue Migrations-Datei für das Hinzufügen der Spalte: role_id:

user@rechner /var/www/html/systeme/testapp $ php artisan make:migration add_column_to_users
Database\Migrations\ - add Column
<?php

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

class AddColumnToUsers extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
      Schema::table('users', function($table)
      {
        $table->integer('role_id')->default('0');
      });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
      Schema::table('users', function($table)
      {
        $table->dropColumn('role_id');
      });
    }
}
Die Funktion up fügt die Spalte hinzu, down könnte diese bei einem "Rollback" wieder entfernen.

Ausführen der Migration

Damit die Änderung in der Datenbank übernommen wird, müssen wir wieder php artisan migrate ausführen:

user@rechner /var/www/html/systeme/tasksscratch $ php artisan migrate
Migration table created successfully.
Migrated: 2014_10_12_000000_create_users_table
Migrated: 2014_10_12_100000_create_password_resets_table
Migrated: 2016_08_04_120955_create_tasks_table
user@rechner /var/www/html/systeme/tasksscratch $ php artisan migrate:status
+------+------------------------------------------------+
| Ran? | Migration                                      |
+------+------------------------------------------------+
| Y    | 2014_10_12_000000_create_users_table           |
| Y    | 2014_10_12_100000_create_password_resets_table |
| Y    | 2016_08_04_120955_create_tasks_table           |
+------+------------------------------------------------+

direktes Anpassen der Datenbank

Jetzt müssen wir noch bestimmte Benutzer zu "Admins" machen:

Für den Anfang gehe ich direkt in die Datenbank und stelle die role_id bei den gewünschten Benutzern um:

php artisan tinker
>>> DB::table('users')->get();
=> [
     {#649
       +"id": 1,
       +"name": "admin",
       +"email": "test@test.test",
       +"password": "?????????????????????????????????????????????????????????x",
       +"remember_token": null,
       +"created_at": "2016-08-18 13:12:24",
       +"updated_at": "2016-08-18 13:12:24",
       +"role_id": 0,
     },
   ]
>>> DB::table('users')->where('name','admin')->update(['role_id'=>1]);
=> 1
>>>

Alternativ: Anpassen der Datenbank mittels Seeds:

Mit Seeds können bestimmte Einträge in eine Datenbank erstellt werden.

Zum Vergleich: Migrationen sind Dateien zum Erstellen der Datenbanktabellen, Seeds sind Dateien um die Datenbank mit bestimmten Werten zu befüllen.

Um einen bereits registrierten Benutzer mit dem Namen "admin" die role_id: 1 zu geben, füge ich folgenden Inhalt in die Datei: database/seeds/DatabaseSeeder.php hinzu:

<?php

use Illuminate\Database\Seeder;

class DatabaseSeeder extends Seeder
{
    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run()
    {
      DB::table('users')->where('name','admin')->update(['role_id'=>1]);
    }
}

Im Anschluss werden Seeds mit folgendem Befehl ausgeführt: 

php artisan db:seed

Das Beispiel macht aus einem Benutzer mit dem Namen "admin" einen Administrator, indem es das Feld role_id auf 1 stellt.

Anpassen des Service-Provider

Als nächsten Schritt wollen wir nur den Admins erlauben, bestimmte Einträge in der Datenbank zu erstellen. Alle registrierten Benutzer dürfen die Einträge lesen. Dazu passe ich den AuthServiceProvider an und führe eine Abfrage mit "isadmin" ein:

in Providers\AuthServiceProvider.php

    public function boot()
    {
        $this->registerPolicies();
        Gate::define('isadmin', function ($user) {
            return $user->role_id == "1";
        });
    }

isadmin kann im Controller und in den Views folgendermaßen verwendet werden:

Verwendung im Controller

use Gate;
    if (Gate::allows('isadmin')) {
       return view('admin');
    }else {
       return view('home');
    }

Hier nochmal der komplette Inhalt von MyinputController.php

Erstellen, Ändern oder Löschen kann nur von einem Admin-Benutzer (mit aktivierter role_id=1) durchgeführt werden:

<?php

namespace App\Http\Controllers;
use App\Myinput;
use View;
use Illuminate\Http\Request;
use App\Http\Requests;
use Gate;

class MyinputController extends Controller
{

  public function __construct()
  {
  $this->middleware('auth');
  }

  /**
      * Display a listing of the resource.
      */
     public function index()
     {
       // get all the myinputs
       $myinputs = Myinput::all();
       // load the view and pass the myinputs

       return View::make('myinputs.index')
           ->with('myinputs', $myinputs);
     }

     /**
      * Show the form for creating a new resource.
      */
     public function create()
     {
       // load the create form (app/views/myinputs/create.blade.php)
       if (Gate::allows('isadmin')) {
         return View::make('myinputs.create');
       } else {
         return back();
       }
     }

     /**
      * Store a newly created resource in storage.
      */
     public function store(Request $request)
     {
       // validate

               $this->validate($request, [
                   'string'       => 'required',
                   'email'      => 'required|email',
                   'integer' => 'required|numeric']);


                   // store
         if (Gate::allows('isadmin')) {
           Myinput::create([
                   'string'      =>  $request->string,
                   'email'      =>  $request->email,
                   'integer' =>  $request->integer,
                 ]);
          }
          return back();

     }

     /**
      * Display the specified resource.
      */
     public function show($id)
     {
       // get the myinput
       $myinput = Myinput::find($id);

       // show the view and pass the myinput to it
       return View::make('myinputs.show')
           ->with('myinput', $myinput);
     }

     /**
      * Show the form for editing the specified resource.
      */
     public function edit($id)
     {
       // get the myinput
       $myinput = Myinput::find($id);

       // show the edit form and pass the myinput
       if (Gate::allows('isadmin')) {
         return View::make('myinputs.edit')
           ->with('myinput', $myinput);
       } else {
         return back();
       }
     }

     /**
      * Update the specified resource in storage.
      */
     public function update(Request $request, $id)
     {
       $this->validate($request, [
           'string'       => 'required',
           'email'      => 'required|email',
           'integer' => 'required|numeric']);

              $myinput = Myinput::find($id);
            if (Gate::allows('isadmin'))   $myinput->update($request->all());

          return back();
     }

     /**
      * Remove the specified resource from storage.
      */
     public function destroy($id)
     {
       // delete
       $myinput = Myinput::find($id);
       if (Gate::allows('isadmin')) $myinput->delete();
       // redirect
        return back();
     }
}

Verwendung in der View

                    @can('isadmin')
                     canadmin
                    @endcan

Beispiel: index.blade.php

<!-- app/views/myinputs/index.blade.php -->
@extends('layouts.app')
@section('content')
<div class="container">
  @can('isadmin')
    <nav class="navbar navbar-inverse">
        <ul class="nav navbar-nav">
            <li><a href="{{ URL::to('myinputs/create') }}">Create a Myinput</a>
        </ul>
    </nav>
  @endcan
<h1>All the Myinputs</h1>

<!-- will be used to show any messages -->
@if (Session::has('message'))
    <div class="alert alert-info">{{ Session::get('message') }}</div>
@endif

<table class="table table-striped table-bordered">
    <thead>
        <tr>
            <td>ID</td>
            <td>String</td>
            <td>Email</td>
            <td>Integer</td>
            <td>Actions</td>
        </tr>
    </thead>
    <tbody>
    @foreach($myinputs as $key => $value)
        <tr>
            <td>{{ $value->id }}</td>
            <td>{{ $value->string }}</td>
            <td>{{ $value->email }}</td>
            <td>{{ $value->integer }}</td>

            <!-- we will also add show, edit, and delete buttons -->
            <td>

                <!-- show the myinput (uses the show method found at GET /myinputs/{id} -->
                <a class="btn btn-small btn-success" href="{{ URL::to('myinputs/' . $value->id) }}">Show this Myinput</a>

                <!-- edit this myinput (uses the edit method found at GET /myinputs/{id}/edit -->
                @can('isadmin')
                  <a class="btn btn-small btn-info" href="{{ URL::to('myinputs/' . $value->id . '/edit') }}">Edit this Myinput</a>
                  <!-- delete the myinput (uses the destroy method DESTROY /myinputs/{id} -->
                  <form action="./myinputs/{{$value->id }}"  onsubmit="return confirm('Are you sure to delete: {{ $value->string}}')" method="POST">
                      {{ csrf_field() }}
                      {{ method_field('DELETE') }}
                      <button type="submit" class="btn btn-danger">
                          <i class="fa fa-btn fa-trash">Delete</i>
                      </button>
                  </form>
                @endcan
            </td>
        </tr>
    @endforeach
    </tbody>
</table>
</div>
@endsection

meldet sich ein Benutzer (ohne role_id=1) an, kann dieser nur die bereits angelegten Einträge ansehen, die Optionen "Create", "Edit" und "Delete" funktionieren nicht und werden auch nicht angezeigt:

Paket - Laravel Permissions

Für Laravel gibt es fertige Pakete für ein Rechtesystem, z.B. https://github.com/spatie/laravel-permission

Laravel Permissions arbeitet vom Prinzip mit folgenden Tabellen:

  • roles
  • permissions
  • role_has_permissions
  • model_has_roles
  • model_has_permissions

Das Paket Laravel Permission deckt hier alle möglichen Szenarien, auch für komplexere Webseiten, ab. So können z.B. Berechtigungen nicht nur auf User, sondern auf beliebige Models gebunden werden. 

Als Beispiel wird eine Rolle, hier angelegt mittels Tinker:

Spatie\Permission\Models\Role::create(['name' => 'writer']);

in die Tabelle "roles" geschrieben:

laravel-authentication-5.jpg?cache=2019-05-1913-07-2650037

die Berechtigung:

Spatie\Permission\Models\Permission::create(['name' => 'edit articles']);

in die Tabelle "permissions":

laravel-authentication-6.jpg?cache=2019-05-1913-08-4950035

die Berechtigung (Permission) kann dann einer Rolle zugewiesen werden: 

$permission=Spatie\Permission\Models\Permission::where('name', 'edit articles')->first();
$permission->assignRole("writer")

in der Tabelle "role_has_permissions"

laravel-authentication-7.jpg?cache=2019-05-1919-21-1145356 

oder direkt einem Benutzer:

App\User::where('name', 'username')->first();
$user->givePermissionTo('edit articles');

 Tabelle "model_has_permissions"

laravel-authentication-8.jpg?cache=2019-05-1919-30-3950057

Bzw. kann einem Benutzer natürlich eine Rolle zugewiesen werden:

$user->assignRole('writer');

Tabelle "model_has_roles"

laravel-authentication-10.jpg?cache=2019-05-1919-42-4250053 

Middleware hinzufügen:

app/Http/Kernel.php :

protected $routeMiddleware = [
    // ...
    'role' => \Spatie\Permission\Middlewares\RoleMiddleware::class,
    'permission' => \Spatie\Permission\Middlewares\PermissionMiddleware::class,
    'role_or_permission' => \Spatie\Permission\Middlewares\RoleOrPermissionMiddleware::class,
];

dies kann dann in den Routes verwendet werden:

Route::group(['middleware' => ['role:super-admin','permission:publish articles']], function () {
    //
});

oder direkt im Controller

public function __construct()
{
    $this->middleware(['role:super-admin','permission:publish articles|edit articles']);
}

Verwendung in routes/web.php

Route::group(['prefix' => '', 'middleware' => 'role:writer'], function()
{
...

Verwenden in Blade-Tempaltes

                                        @role('admin')
                                            <a class="nav-link" href="/admin">Admin</a>
                                        @endrole

 

positive Bewertung({{pro_count}})
Beitrag bewerten:
{{percentage}} % positiv
negative Bewertung({{con_count}})

DANKE für deine Bewertung!

Fragen / Kommentare


(sortiert nach Bewertung / Datum) [alle Kommentare(neueste zuerst)]

✍anonym
21.06.2018 10:59
User: Tom 
Guter Artikel! Hat mehr sehr geholfen als ich nach einem Rollensystem für Laravel gesucht habe :-)