L’authentification vérifie « qui » est l’utilisateur (par exemple via la connexion).
L’autorisation décide « ce qu’il peut faire » : n’éditer que ses propres publications, seuls les administrateurs modifient les paramètres, etc.Après avoir implémenté la connexion via l’authentification, l’étape suivante est l’autorisation.
Laravel propose deux approches d’autorisation : les Gates et les Policies. Le choix dépend de votre cas d’usage.
Dans un contrôleur, utilisez Gate::allows() ou Gate::denies().
use Illuminate\Support\Facades\Gate;public function update(Request $request, Post $post): RedirectResponse{ if (! Gate::allows('update-post', $post)) { abort(403); } // Mise à jour... return redirect('/posts');}
Pour retourner automatiquement une réponse 403 en cas d’échec, utilisez Gate::authorize() (pas besoin d’écrire abort(403)).
public function update(Request $request, Post $post): RedirectResponse{ Gate::authorize('update-post', $post); // Ici la permission est accordée // Mise à jour... return redirect('/posts');}
Gate::authorize() lève Illuminate\Auth\Access\AuthorizationException en cas de refus, que Laravel convertit automatiquement en 403.
Pour donner tous les droits aux administrateurs, utilisez Gate::before().
use App\Models\User;use Illuminate\Support\Facades\Gate;public function boot(): void{ // $ability contient le nom de la Gate en cours ('update-post', etc.) Gate::before(function (User $user, string $ability) { if ($user->isAdministrator()) { return true; } }); Gate::define('update-post', function (User $user, Post $post) { return $user->id === $post->user_id; });}
Si la closure before retourne une valeur non nulle, cette valeur devient le résultat. Si elle retourne null (ou rien), le contrôle normal se poursuit.
Dans les templates, les directives @can et @cannot sont pratiques.
@can('update-post', $post) <a href="{{ route('posts.edit', $post) }}">Modifier</a>@endcan@cannot('update-post', $post) <p>Vous n'avez pas la permission de modifier cette publication.</p>@endcannot
Une Policy regroupe la logique d’autorisation d’un modèle particulier dans une classe. Pour gérer les droits CRUD sur Post, les Policies conviennent mieux que les Gates.
Une Policy générée avec --model contient les méthodes CRUD standard.
<?phpnamespace App\Policies;use App\Models\Post;use App\Models\User;class PostPolicy{ /** * Peut consulter la liste des publications ? */ public function viewAny(User $user): bool { return true; // Tout utilisateur authentifié } /** * Peut consulter une publication ? */ public function view(User $user, Post $post): bool { return true; } /** * Peut créer une publication ? */ public function create(User $user): bool { return true; } /** * Peut mettre à jour une publication ? */ public function update(User $user, Post $post): bool { return $user->id === $post->user_id; // Uniquement ses propres publications } /** * Peut supprimer une publication ? */ public function delete(User $user, Post $post): bool { return $user->id === $post->user_id; }}
/** * Contrôle préalable exécuté avant les autres méthodes. * $ability contient le nom de la méthode ('update', 'delete'...). */public function before(User $user, string $ability): bool|null{ if ($user->isAdministrator()) { return true; // Tout est permis pour l'admin } return null; // Poursuivre avec la méthode normale}
before n’est appelé que si la méthode correspondante existe dans la Policy. Sans méthode update, before n’est pas appelé non plus.
Le contrôleur de base Laravel expose authorize() (utilisez Gate::authorize() sinon).
Gate::authorize()
User::can() / cannot()
<?phpnamespace App\Http\Controllers;use App\Models\Post;use Illuminate\Http\RedirectResponse;use Illuminate\Http\Request;use Illuminate\Support\Facades\Gate;class PostController extends Controller{ public function update(Request $request, Post $post): RedirectResponse { Gate::authorize('update', $post); // Mise à jour... return redirect()->route('posts.index'); } public function store(Request $request): RedirectResponse { Gate::authorize('create', Post::class); // Création... return redirect()->route('posts.index'); } public function destroy(Post $post): RedirectResponse { Gate::authorize('delete', $post); $post->delete(); return redirect()->route('posts.index'); }}
<?phpnamespace App\Http\Controllers;use App\Models\Post;use Illuminate\Http\RedirectResponse;use Illuminate\Http\Request;class PostController extends Controller{ public function update(Request $request, Post $post): RedirectResponse { if ($request->user()->cannot('update', $post)) { abort(403); } // Mise à jour... return redirect()->route('posts.index'); }}
Pour vérifier au niveau de la route, utilisez le middleware can.
use App\Models\Post;// Accessible aux utilisateurs autorisés à mettre à jourRoute::put('/posts/{post}', [PostController::class, 'update']) ->middleware('can:update,post');// Accessible aux utilisateurs autorisés à créerRoute::post('/posts', [PostController::class, 'store']) ->middleware('can:create,App\Models\Post');
Gate : jugements simples non liés à un modèle (accès au dashboard admin, droit global de configuration).
Policy : gestion CRUD sur un modèle (Post, Order, Comment, etc.).
APIs fréquentes
// GateGate::allows('update-post', $post); // true/falseGate::denies('update-post', $post); // true/falseGate::authorize('update-post', $post); // exception en cas d'échec// Depuis le modèle utilisateur$user->can('update', $post); // true/false$user->cannot('update', $post); // true/false// Dans un contrôleurGate::authorize('update', $post); // 403 si refus// Dans Blade@can('update', $post) ... @endcan@cannot('update', $post) ... @endcannot
Commandes Artisan utiles
# Policy videphp artisan make:policy PostPolicy# Avec méthodes CRUD pour un modèlephp artisan make:policy PostPolicy --model=Post