Authentification avec Rôles - Laravel
Étape 1:Configuration de Base
installation des dépendances nécessaires.
Étape 2: Création des Modèles et des Migrations
Création du modèle Role
app/Models/Role.php
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use App\Models\User;
class Role extends Model
{
use HasFactory;
protected $primaryKey = "idRole";
protected $table = "roles";
protected $fillable = ['name'];
public function users()
{
return $this->hasMany(User::class, 'idRole');
}
}
Mise à jour du modèle User
app/Models/User.php
<?php
namespace App\Models;
// use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Sanctum\HasApiTokens;
class User extends Authenticatable
{
use HasApiTokens, HasFactory, Notifiable;
protected $fillable = [
'name',
'prenom',
'idRole',
'active',
'email',
'password',
];
public function role()
{
return $this->belongsTo(Role::class, 'idRole', 'idRole');
}
protected $hidden = [
'password',
'remember_token',
];
protected $casts = [
'email_verified_at' => 'datetime',
'password' => 'hashed',
];
}
Création de la migration pour les rôles
database/migrations/xxxx_xx_xx_create_roles_table.php
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up()
{
Schema::create('roles', function (Blueprint $table) {
$table->id('idRole');
$table->string('name');
$table->timestamps();
});
// Ajout de la colonne idRole à la table users
Schema::table('users', function (Blueprint $table) {
$table->unsignedBigInteger('idRole')->nullable()->after('email');
$table->boolean('active')->default(true)->after('idRole');
$table->foreign('idRole')->references('idRole')->on('roles');
});
}
public function down()
{
Schema::table('users', function (Blueprint $table) {
$table->dropForeign(['idRole']);
$table->dropColumn(['idRole', 'active']);
});
Schema::dropIfExists('roles');
}
};
Étape 3: Création du Seeder pour les Rôles
database/seeders/RoleSeeder.php
<?php
namespace Database\Seeders;
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\DB;
class RoleSeeder extends Seeder
{
public function run()
{
DB::table('roles')->insert([
['name' => 'Admin'],
['name' => 'Editor'],
['name' => 'User']
]);
}
}
Étape 4: Création du Middleware pour les Rôles
app/Http/Middleware/CheckRole.php
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
class CheckRole
{
public function handle(Request $request, Closure $next, ...$roles)
{
if (!Auth::check()) {
return redirect('/login');
}
$user = Auth::user();
$userRole = $user->role->name ?? null;
if (!in_array($userRole, $roles)) {
abort(403, 'Accès non autorisé');
}
return $next($request);
}
}
Enregistrement du middleware dans le Kernel
app/Http/Kernel.php
protected $routeMiddleware = [
// ... autres middlewares
'role' => \App\Http\Middleware\CheckRole::class,
];
Étape 5: Création des Contrôleurs
Contrôleur principal pour les administrateurs
app/Http/Controllers/AdminController.php
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use App\Models\User;
use App\Models\Role;
class AdminController extends Controller
{
public function __construct()
{
$this->middleware(['auth', 'role:Admin']);
}
public function dashboard()
{
$users = User::with('role')->get();
return view('admin.dashboard', compact('users'));
}
public function manageUsers()
{
$users = User::with('role')->paginate(10);
$roles = Role::all();
return view('admin.users', compact('users', 'roles'));
}
public function updateUserRole(Request $request, $id)
{
$user = User::findOrFail($id);
$user->idRole = $request->role_id;
$user->save();
return redirect()->back()->with('success', 'Rôle mis à jour avec succès');
}
public function toggleUserStatus($id)
{
$user = User::findOrFail($id);
$user->active = !$user->active;
$user->save();
return redirect()->back()->with('success', 'Statut utilisateur mis à jour');
}
}
Contrôleur pour les éditeurs
app/Http/Controllers/EditorController.php
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
class EditorController extends Controller
{
public function __construct()
{
$this->middleware(['auth', 'role:Admin,Editor']);
}
public function dashboard()
{
return view('editor.dashboard');
}
public function contentManagement()
{
return view('editor.content');
}
}
Étape 6: Définition des Routes
routes/web.php
<?php
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\AdminController;
use App\Http\Controllers\EditorController;
use Illuminate\Support\Facades\Auth;
// Routes d'authentification par défaut
Auth::routes();
// Routes protégées par authentification
Route::get('/home', [App\Http\Controllers\HomeController::class, 'index'])->name('home');
// Routes pour les administrateurs uniquement
Route::prefix('admin')->middleware(['auth', 'role:Admin'])->group(function () {
Route::get('/dashboard', [AdminController::class, 'dashboard'])->name('admin.dashboard');
Route::get('/users', [AdminController::class, 'manageUsers'])->name('admin.users');
Route::put('/users/{id}/role', [AdminController::class, 'updateUserRole'])->name('admin.update.user.role');
Route::put('/users/{id}/status', [AdminController::class, 'toggleUserStatus'])->name('admin.toggle.user.status');
});
// Routes pour les éditeurs et administrateurs
Route::prefix('editor')->middleware(['auth', 'role:Admin,Editor'])->group(function () {
Route::get('/dashboard', [EditorController::class, 'dashboard'])->name('editor.dashboard');
Route::get('/content', [EditorController::class, 'contentManagement'])->name('editor.content');
});
// Route pour tous les utilisateurs authentifiés
Route::get('/profile', function () {
$user = Auth::user();
return view('profile', compact('user'));
})->middleware('auth')->name('profile');
// Route pour la déconnexion
Route::post('/logout', function () {
Auth::logout();
return redirect('/login');
})->middleware('auth')->name('logout');
Étape 7: Création des Vues
Menu de navigation avec gestion des rôles
resources/views/layouts/app.blade.php
<!doctype html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
<title>@yield('title', 'Application avec Rôles')</title>
</head>
<body>
<nav class="navbar navbar-expand-md navbar-dark bg-dark">
<div class="container">
<a class="navbar-brand" href="{{ url('/') }}">
Mon Application
</a>
<div class="navbar-nav ms-auto">
@guest
<a class="nav-link" href="{{ route('login') }}">Connexion</a>
<a class="nav-link" href="{{ route('register') }}">Inscription</a>
@else
<span class="navbar-text me-3">
Bonjour, {{ Auth::user()->name }}
</span>
@if(Auth::user()->role->name === 'Admin')
<a class="nav-link" href="{{ route('admin.dashboard') }}">Admin</a>
@elseif(Auth::user()->role->name === 'Editor')
<a class="nav-link" href="{{ route('editor.dashboard') }}">Éditeur</a>
@endif
<a class="nav-link" href="{{ route('profile') }}">Profil</a>
<form method="POST" action="{{ route('logout') }}" class="d-inline">
@csrf
<button type="submit" class="btn btn-link nav-link">Déconnexion</button>
</form>
@endguest
</div>
</div>
</nav>
<main class="py-4">
@yield('content')
</main>
</body>
</html>
Vue du tableau de bord administrateur
resources/views/admin/dashboard.blade.php
@extends('layouts.app')
@section('title', 'Tableau de Bord Administrateur')
@section('content')
<div class="container">
<div class="row">
<div class="col-md-12">
<h1>Tableau de Bord Administrateur</h1>
<p>Bienvenue {{ Auth::user()->name }}, vous avez des droits d'administrateur.</p>
<div class="card">
<div class="card-header">
Gestion des Utilisateurs
</div>
<div class="card-body">
<table class="table">
<thead>
<tr>
<th>Nom</th>
<th>Email</th>
<th>Rôle</th>
<th>Statut</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
@foreach($users as $user)
<tr>
<td>{{ $user->name }}</td>
<td>{{ $user->email }}</td>
<td>{{ $user->role->name ?? 'Aucun' }}</td>
<td>
@if($user->active)
<span class="badge bg-success">Actif</span>
@else
<span class="badge bg-danger">Inactif</span>
@endif
</td>
<td>
<form method="POST" action="{{ route('admin.toggle.user.status', $user->id) }}" class="d-inline">
@csrf
@method('PUT')
<button type="submit" class="btn btn-sm btn-secondary">
@if($user->active) Désactiver @else Activer @endif
</button>
</form>
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
@endsection
Vue du tableau de bord éditeur
resources/views/editor/dashboard.blade.php
@extends('layouts.app')
@section('title', 'Tableau de Bord Éditeur')
@section('content')
<div class="container">
<div class="row">
<div class="col-md-12">
<h1>Tableau de Bord Éditeur</h1>
<p>Bonjour {{ Auth::user()->name }}, vous avez des droits d'édition.</p>
<div class="card">
<div class="card-body">
<a href="{{ route('editor.content') }}" class="btn btn-primary">
Gérer le Contenu
</a>
</div>
</div>
</div>
</div>
</div>
@endsection
Étape 8: Configuration Supplémentaire
Mise à jour du contrôleur d'inscription pour assigner un rôle par défaut
app/Http/Controllers/Auth/RegisterController.php
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use App\Providers\RouteServiceProvider;
use App\Models\User;
use Illuminate\Foundation\Auth\RegistersUsers;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Validator;
class RegisterController extends Controller
{
use RegistersUsers;
protected $redirectTo = RouteServiceProvider::HOME;
public function __construct()
{
$this->middleware('guest');
}
protected function validator(array $data)
{
return Validator::make($data, [
'name' => ['required', 'string', 'max:255'],
'prenom' => ['required', 'string', 'max:255'],
'email' => ['required', 'string', 'email', 'max:255', 'unique:users'],
'password' => ['required', 'string', 'min:8', 'confirmed'],
]);
}
protected function create(array $data)
{
return User::create([
'name' => $data['name'],
'prenom' => $data['prenom'],
'email' => $data['email'],
'idRole' => 3, // Rôle par défaut: User
'active' => true,
'password' => Hash::make($data['password']),
]);
}
}
Test de l'application
# Lancer le serveur
php artisan serve
# Accéder aux différentes pages :
# - http://127.0.0.1:8000/register - Inscription
# - http://127.0.0.1:8000/login - Connexion
# - http://127.0.0.1:8000/admin/dashboard - Tableau de bord admin (rôle requis)
# - http://127.0.0.1:8000/editor/dashboard - Tableau de bord éditeur (rôle requis)
Notes de Sécurité Importantes
- Valider toujours les rôles côté serveur, jamais uniquement côté client
- Implémenter des vérifications d'autorisation pour chaque opération sensible
- Utiliser des politiques (Policies) pour une logique d'autorisation complexe
- Vérifier régulièrement les affectations de rôles et les permissions
- Assurer que les changements de rôle sont journalisés pour le suivi de sécurité
- Protéger les routes sensibles avec des middlewares appropriés