Middleware
php artisan make:middleware CheckRole
// app/Http/Middleware/CheckRole.php
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 ...$roles): Response
{
if (!$request->user()) {
return response()->json(['error' => 'Non authentifié'], 401);
}
if (!in_array($request->user()->role, $roles)) {
return response()->json(['error' => 'AccÚs refusé'], 403);
}
return $next($request); // Laisser passer la requĂȘte
}
}
// Middleware aprÚs la réponse (response middleware)
class AddResponseTime
{
public function handle(Request $request, Closure $next): Response
{
$start = microtime(true);
$response = $next($request);
$time = round((microtime(true) - $start) * 1000, 2);
return $response->header('X-Response-Time', "{$time}ms");
}
}
// Enregistrement dans bootstrap/app.php (Laravel 11)
return Application::configure(basePath: dirname(__DIR__))
->withMiddleware(function (Middleware $middleware) {
// Alias pour utilisation dans les routes
$middleware->alias([
'role' => \App\Http\Middleware\CheckRole::class,
'verified' => \App\Http\Middleware\EnsureEmailIsVerified::class,
]);
// Middleware global (toutes les requĂȘtes)
$middleware->append(\App\Http\Middleware\AddResponseTime::class);
// Groupes
$middleware->group('api', [
\Illuminate\Routing\Middleware\ThrottleRequests::class.':api',
]);
});
// Utilisation dans les routes
Route::middleware(['auth', 'role:admin'])->group(function() {
Route::get('/admin', [AdminController::class, 'index']);
});
Route::middleware('role:admin,editor')->get('/dashboard', fn() => view('dashboard'));
Laravel Breeze
composer require laravel/breeze --dev
php artisan breeze:install blade # avec Blade + Tailwind
# ou
php artisan breeze:install api # API headless (SPA)
npm install && npm run dev
php artisan migrate
Breeze génÚre automatiquement :
- Routes :
/login,/register,/forgot-password,/reset-password,/verify-email,/logout - Controllers dans
app/Http/Controllers/Auth/ - Vues Blade dans
resources/views/auth/ - Migration
usersdĂ©jĂ prĂȘte
Sessions & Flash
// Stocker en session
session()->put('user_preferences', ['theme' => 'dark']);
session(['user_id' => 42]); // équivalent
// Lire
$prefs = session('user_preferences');
$prefs = session('user_preferences.theme', 'light'); // dot notation + défaut
// Vérifier
session()->has('user_preferences'); // clé existe
session()->exists('user_preferences'); // clĂ© existe mĂȘme si null
// Supprimer
session()->forget('user_preferences');
session()->flush(); // tout vider
// Flash data (disponible une seule fois, pour la prochaine requĂȘte)
return redirect()->back()->with('success', 'Article créé !');
// dans la vue : session('success')
// Flash dans le controller
session()->flash('success', 'Opération réussie');
// Reflash (garder pour une requĂȘte supplĂ©mentaire)
session()->reflash();
session()->keep(['success', 'error']); // conserver certaines clés
Guards & Auth
// Helpers Auth
$user = auth()->user(); // utilisateur courant (null si non auth)
$id = auth()->id(); // ID de l'utilisateur
$isAuth = auth()->check(); // est connecté ?
$isGuest = auth()->guest(); // est visiteur ?
// Avec guard spécifique
$adminUser = auth()->guard('admin')->user();
// Connexion manuelle
auth()->login($user); // connecter
auth()->login($user, true); // avec "se souvenir de moi"
auth()->loginUsingId(42);
auth()->logout();
// Tentative de connexion
if (auth()->attempt(['email' => $email, 'password' => $password])) {
request()->session()->regenerate();
return redirect()->intended('/dashboard');
}
// â retourne false si identifiants incorrects
// Depuis un controller
$user = $request->user(); // plus explicite
Gates
// Définir dans AppServiceProvider::boot()
use Illuminate\Support\Facades\Gate;
Gate::define('update-article', function(User $user, Article $article) {
return $user->id === $article->author_id || $user->isAdmin();
});
Gate::define('admin-area', fn(User $user) => $user->role === 'admin');
// Vérifier dans un controller
public function update(Request $request, Article $article): JsonResponse {
// LÚve AuthorizationException (403) si refusé
$this->authorize('update-article', $article);
$article->update($request->validated());
return response()->json(['data' => $article]);
}
// Vérifier sans exception
if (Gate::allows('update-article', $article)) {
// autorisé
}
if (Gate::denies('admin-area')) {
abort(403);
}
// Dans Blade
@can('update-article', $article)
<a href="...">Modifier</a>
@endcan
@cannot('admin-area')
<p>Zone réservée aux admins</p>
@endcannot
Policies
php artisan make:policy ArticlePolicy --model=Article
// app/Policies/ArticlePolicy.php
namespace App\Policies;
use App\Models\Article;
use App\Models\User;
class ArticlePolicy
{
// Court-circuite tout pour les admins
public function before(User $user): ?bool {
if ($user->isAdmin()) return true;
return null; // continue vers les méthodes spécifiques
}
public function viewAny(?User $user): bool {
return true; // tout le monde peut lister
}
public function view(?User $user, Article $article): bool {
// PubliĂ© â public. Brouillon â auteur seulement
return $article->isPublished() || $user?->id === $article->author_id;
}
public function create(User $user): bool {
return in_array($user->role, ['author', 'editor', 'admin']);
}
public function update(User $user, Article $article): bool {
return $user->id === $article->author_id || $user->role === 'editor';
}
public function delete(User $user, Article $article): bool {
return $user->id === $article->author_id;
}
}
// Utilisation dans le controller
$this->authorize('update', $article); // auto-détecte ArticlePolicy
$this->authorize('create', Article::class);
// Dans le middleware de route
Route::put('/articles/{article}', [ArticleController::class, 'update'])
->can('update', 'article'); // â auto-dĂ©tecte ArticlePolicy::update
RĂŽles RBAC
// Colonne role sur la table users
// $table->string('role')->default('user');
// Méthodes helpers dans User model
class User extends Authenticatable
{
public function isAdmin(): bool { return $this->role === 'admin'; }
public function isEditor(): bool { return in_array($this->role, ['editor', 'admin']); }
public function isAuthor(): bool { return in_array($this->role, ['author', 'editor', 'admin']); }
public function hasRole(string ...$roles): bool {
return in_array($this->role, $roles);
}
}
// Middleware RoleMiddleware
public function handle(Request $request, Closure $next, string ...$roles): Response {
if (!$request->user()?->hasRole(...$roles)) {
abort(403, 'RĂŽle insuffisant.');
}
return $next($request);
}
Pour des besoins complexes (permissions granulaires, rĂŽles multiples par utilisateur), installe le package
spatie/laravel-permission : composer require spatie/laravel-permission.
Laravel Sanctum
composer require laravel/sanctum
php artisan vendor:publish --provider="Laravel\Sanctum\SanctumServiceProvider"
php artisan migrate
// User model : ajouter le trait
use Laravel\Sanctum\HasApiTokens;
class User extends Authenticatable {
use HasApiTokens, HasFactory, Notifiable;
}
// Controller d'authentification API
class AuthController extends Controller {
public function login(Request $request): JsonResponse {
$credentials = $request->validate([
'email' => 'required|email',
'password' => 'required',
]);
if (!auth()->attempt($credentials)) {
return response()->json(['message' => 'Identifiants incorrects'], 401);
}
$user = auth()->user();
$token = $user->createToken('api-token', ['read', 'write']); // abilities
return response()->json([
'token' => $token->plainTextToken,
'token_type' => 'Bearer',
'expires_at' => null, // ou now()->addDays(30)
]);
}
public function logout(Request $request): JsonResponse {
$request->user()->currentAccessToken()->delete(); // token courant
// $request->user()->tokens()->delete(); // tous les tokens
return response()->json(['message' => 'Déconnecté']);
}
public function me(Request $request): JsonResponse {
return response()->json(['data' => $request->user()]);
}
}
// Routes protégées par Sanctum
Route::middleware('auth:sanctum')->group(function() {
Route::get('/me', [AuthController::class, 'me']);
Route::post('/logout', [AuthController::class, 'logout']);
Route::apiResource('articles', ArticleController::class)->except(['index', 'show']);
});
// Vérifier les abilities
$request->user()->tokenCan('write');
if (!$request->user()->tokenCan('delete')) abort(403);