L’autenticazione (Authentication) verifica “chi è” l’utente. Il login ne è l’esempio tipico.
L’autorizzazione (Authorization) decide “cosa può fare” quell’utente. Ad esempio: modificare solo i propri post, solo gli amministratori possono modificare le impostazioni, ecc.Dopo aver implementato la funzione di login con l’autenticazione, il passo successivo è l’autorizzazione.
Le funzionalità di autorizzazione di Laravel offrono due approcci: Gate e Policy. Quale usare dipende dal caso d’uso.
Criteri di scelta
Scenario
Consigliato
Verifica semplice non legata a un modello
Gate
Gestione dei permessi CRUD su un modello
Policy
Controllo dell’accesso alla dashboard admin
Gate
Diritti di pubblicazione, modifica ed eliminazione di articoli di blog
Definisci le Gate nel metodo boot di App\Providers\AppServiceProvider con Gate::define().
// app/Providers/AppServiceProvider.phpuse App\Models\Post;use App\Models\User;use Illuminate\Support\Facades\Gate;public function boot(): void{ Gate::define('update-post', function (User $user, Post $post) { return $user->id === $post->user_id; });}
La closure della Gate riceve sempre come primo argomento l’utente attualmente autenticato. Dal secondo argomento in poi passi eventuali informazioni aggiuntive come il modello di riferimento.
In un controller, per verificare i permessi con una Gate usa Gate::allows() o Gate::denies().
use Illuminate\Support\Facades\Gate;public function update(Request $request, Post $post): RedirectResponse{ if (! Gate::allows('update-post', $post)) { abort(403); } // Logica di aggiornamento del post... return redirect('/posts');}
Per restituire automaticamente una risposta 403 in mancanza di permessi usa Gate::authorize(). Elimina la necessità di scrivere abort(403).
public function update(Request $request, Post $post): RedirectResponse{ Gate::authorize('update-post', $post); // Se hai i permessi arrivi qui // Logica di aggiornamento del post... return redirect('/posts');}
Gate::authorize() lancia una Illuminate\Auth\Access\AuthorizationException in mancanza di permessi. Laravel la converte automaticamente in una risposta HTTP 403.
Se vuoi concedere tutti i permessi agli amministratori usa Gate::before().
use App\Models\User;use Illuminate\Support\Facades\Gate;public function boot(): void{ // $ability contiene il nome della gate in corso di verifica ('update-post', ecc.) 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; });}
Se la closure di before restituisce un valore diverso da null, quel valore diventa la decisione finale. Se restituisce null o niente, si procede alla valutazione normale della Gate.
Nei template le direttive @can e @cannot sono comode per usare le Gate.
@can('update-post', $post) <a href="{{ route('posts.edit', $post) }}">Modifica</a>@endcan@cannot('update-post', $post) <p>Non hai i permessi per modificare questo post.</p>@endcannot
Le Policy raccolgono in una classe la logica di autorizzazione relativa a un modello specifico. Per gestire i permessi CRUD sul modello Post, le Policy sono più adatte delle Gate.
Una Policy generata con --model contiene i metodi corrispondenti alle azioni CRUD standard.
<?phpnamespace App\Policies;use App\Models\Post;use App\Models\User;class PostPolicy{ /** * Può visualizzare l'elenco dei post */ public function viewAny(User $user): bool { return true; // Tutti gli utenti autenticati possono visualizzare } /** * Può visualizzare un post specifico */ public function view(User $user, Post $post): bool { return true; // Tutti gli utenti autenticati possono visualizzare } /** * Può creare un post */ public function create(User $user): bool { return true; // Tutti gli utenti autenticati possono creare } /** * Può aggiornare un post */ public function update(User $user, Post $post): bool { return $user->id === $post->user_id; // Solo il proprio post } /** * Può eliminare un post */ public function delete(User $user, Post $post): bool { return $user->id === $post->user_id; // Solo il proprio post }}
Anche nelle Policy puoi definire un metodo before per concedere tutti i permessi agli amministratori.
/** * Verifica preliminare eseguita prima degli altri metodi della Policy * $ability contiene il nome del metodo Policy ('update', 'delete', ecc.) */public function before(User $user, string $ability): bool|null{ if ($user->isAdministrator()) { return true; // L'amministratore ha tutti i permessi } return null; // Restituendo null si passa al normale metodo della Policy}
Il metodo before viene invocato solo se la Policy contiene il metodo corrispondente. Se ad esempio manca update, before non viene chiamato.
Per effettuare il controllo di autorizzazione a livello di route usa il middleware can.
use App\Models\Post;// Accessibile solo agli utenti con permesso di aggiornare il postRoute::put('/posts/{post}', [PostController::class, 'update']) ->middleware('can:update,post');// Accessibile solo agli utenti con permesso di creare postRoute::post('/posts', [PostController::class, 'store']) ->middleware('can:create,App\Models\Post');
Una volta registrate le Policy, anche @can / @cannot in Blade usano automaticamente le Policy.
{{-- Il pulsante di modifica del post viene mostrato solo agli utenti con i permessi --}}@can('update', $post) <a href="{{ route('posts.edit', $post) }}" class="btn">Modifica</a>@endcan{{-- Lo stesso per il pulsante di eliminazione --}}@can('delete', $post) <form method="POST" action="{{ route('posts.destroy', $post) }}"> @csrf @method('DELETE') <button type="submit">Elimina</button> </form>@endcan{{-- Pulsante di creazione nuovo post --}}@can('create', App\Models\Post::class) <a href="{{ route('posts.create') }}">Nuovo post</a>@endcan
Esempio pratico: gestione dei permessi degli articoli di blog
Esempio che implementa “solo l’autore può modificare e cancellare i propri articoli” in un’app blog.
1
Genera la Policy
php artisan make:policy PostPolicy --model=Post
2
Implementa i metodi della Policy
<?phpnamespace App\Policies;use App\Models\Post;use App\Models\User;class PostPolicy{ /** * Concedi tutti i permessi all'amministratore */ public function before(User $user, string $ability): bool|null { if ($user->is_admin) { return true; } return null; } public function viewAny(User $user): bool { return true; } public function view(User $user, Post $post): bool { return true; } public function create(User $user): bool { return true; } public function update(User $user, Post $post): bool { return $user->id === $post->user_id; } public function delete(User $user, Post $post): bool { return $user->id === $post->user_id; }}
3
Aggiungi authorizeResource() al controller
<?phpnamespace App\Http\Controllers;use App\Models\Post;use Illuminate\Http\RedirectResponse;use Illuminate\Http\Request;use Illuminate\View\View;class PostController extends Controller{ public function __construct() { $this->authorizeResource(Post::class, 'post'); } public function index(): View { $posts = Post::latest()->paginate(10); return view('posts.index', compact('posts')); } public function store(Request $request): RedirectResponse { $post = $request->user()->posts()->create( $request->validate([ 'title' => ['required', 'string', 'max:255'], 'body' => ['required', 'string'], ]) ); return redirect()->route('posts.show', $post); } public function update(Request $request, Post $post): RedirectResponse { $post->update( $request->validate([ 'title' => ['required', 'string', 'max:255'], 'body' => ['required', 'string'], ]) ); return redirect()->route('posts.show', $post); } public function destroy(Post $post): RedirectResponse { $post->delete(); return redirect()->route('posts.index'); }}
4
Aggiungi i controlli di permesso nel template Blade
Gate: verifiche di permessi semplici non legate a un modello. Accesso alla dashboard admin, modifica di impostazioni globali, ecc.
Policy: gestione dei permessi CRUD su un modello. Crei una Policy per ciascuna risorsa (Post, Order, Comment, ecc.).
Riepilogo delle API più usate
// Controllo con GateGate::allows('update-post', $post); // true/falseGate::denies('update-post', $post); // true/falseGate::authorize('update-post', $post); // eccezione in caso di fallimento// Controllo dal modello utente$user->can('update', $post); // true/false$user->cannot('update', $post); // true/false// Controllo dal controllerGate::authorize('update', $post); // 403 in caso di fallimento// Controllo in Blade@can('update', $post) ... @endcan@cannot('update', $post) ... @endcannot
Comandi Artisan più usati
# Genera una Policy vuotaphp artisan make:policy PostPolicy# Genera con i metodi CRUD legati al modellophp artisan make:policy PostPolicy --model=Post