Vai al contenuto principale

Differenza tra autenticazione e autorizzazione

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
ScenarioConsigliato
Verifica semplice non legata a un modelloGate
Gestione dei permessi CRUD su un modelloPolicy
Controllo dell’accesso alla dashboard adminGate
Diritti di pubblicazione, modifica ed eliminazione di articoli di blogPolicy

Gate

Le Gate sono verifiche di autorizzazione semplici basate su closure. Adatte a valutazioni di permessi non legate a un modello specifico.

Flusso di autorizzazione tramite Gate

Definire una Gate

Definisci le Gate nel metodo boot di App\Providers\AppServiceProvider con Gate::define().
// app/Providers/AppServiceProvider.php

use 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.

Verificare i permessi con una Gate

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');
}

Lanciare eccezioni con Gate::authorize()

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.

Bypass amministratore (metodo before)

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.

@can / @cannot nei template Blade

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

Policy

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.

Flusso di autorizzazione tramite Policy

Generare una Policy

Genera una classe Policy con il comando Artisan make:policy.
php artisan make:policy PostPolicy
Per generare uno scheletro che include tutti i metodi CRUD corrispondenti a un modello, usa l’opzione --model.
php artisan make:policy PostPolicy --model=Post
Viene generato app/Policies/PostPolicy.php.

Rilevamento automatico di modelli e Policy

Di default Laravel rileva automaticamente le Policy secondo una convenzione di naming.
  • Modello: app/Models/Post.php
  • Policy: app/Policies/PostPolicy.php
Rispettando questa convenzione non è necessario registrare la Policy.
Se non segui la convenzione o vuoi registrarla manualmente, usa Gate::policy() nel metodo boot di AppServiceProvider.
use App\Models\Post;
use App\Policies\PostPolicy;
use Illuminate\Support\Facades\Gate;

Gate::policy(Post::class, PostPolicy::class);
In Laravel 13 puoi anche registrarla in modo dichiarativo con l’attribute #[UsePolicy] sul modello.
use App\Policies\PostPolicy;
use Illuminate\Database\Eloquent\Attributes\UsePolicy;

#[UsePolicy(PostPolicy::class)]
class Post extends Model {}

Implementare i metodi di una Policy

Una Policy generata con --model contiene i metodi corrispondenti alle azioni CRUD standard.
<?php

namespace 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
    }
}

Bypass amministratore (metodo before)

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.

Ordine di esecuzione della callback before

Usare le Policy nei controller

Metodo authorize()

I controller Laravel dispongono dell’helper authorize() (se non usi il controller base, usa Gate::authorize()).
<?php

namespace 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);

        // Logica di aggiornamento del post...

        return redirect()->route('posts.index');
    }

    public function store(Request $request): RedirectResponse
    {
        Gate::authorize('create', Post::class);

        // Logica di creazione del post...

        return redirect()->route('posts.index');
    }

    public function destroy(Post $post): RedirectResponse
    {
        Gate::authorize('delete', $post);

        $post->delete();

        return redirect()->route('posts.index');
    }
}

Registrazione in blocco delle Policy RESTful con authorizeResource()

Chiamando authorizeResource() nel costruttore, i metodi della Policy corrispondenti alle azioni del controller vengono collegati automaticamente.
<?php

namespace App\Http\Controllers;

use App\Models\Post;

class PostController extends Controller
{
    public function __construct()
    {
        $this->authorizeResource(Post::class, 'post');
    }

    // A index(), show(), create(), store(), edit(), update(), destroy()
    // vengono applicati automaticamente i metodi Policy corrispondenti
}
Corrispondenza tra azioni del controller e metodi della Policy:
Azione del controllerMetodo della Policy
indexviewAny
showview
create / storecreate
edit / updateupdate
destroydelete
Con authorizeResource() non devi scrivere authorize() in ogni azione. Comodo con i controller di risorsa RESTful.

Autorizzazione tramite middleware

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 post
Route::put('/posts/{post}', [PostController::class, 'update'])
    ->middleware('can:update,post');

// Accessibile solo agli utenti con permesso di creare post
Route::post('/posts', [PostController::class, 'store'])
    ->middleware('can:create,App\Models\Post');
Scrittura più concisa con il metodo can:
Route::put('/posts/{post}', [PostController::class, 'update'])
    ->can('update', 'post');

Usare le Policy nei template Blade

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

<?php

namespace 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

<?php

namespace 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

{{-- resources/views/posts/show.blade.php --}}
<h1>{{ $post->title }}</h1>
<p>{{ $post->body }}</p>
<p>Autore: {{ $post->user->name }}</p>

@can('update', $post)
    <a href="{{ route('posts.edit', $post) }}">Modifica</a>
@endcan

@can('delete', $post)
    <form method="POST" action="{{ route('posts.destroy', $post) }}">
        @csrf
        @method('DELETE')
        <button type="submit">Elimina</button>
    </form>
@endcan
  • 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.).
// Controllo con Gate
Gate::allows('update-post', $post);      // true/false
Gate::denies('update-post', $post);      // true/false
Gate::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 controller
Gate::authorize('update', $post);        // 403 in caso di fallimento

// Controllo in Blade
@can('update', $post) ... @endcan
@cannot('update', $post) ... @endcannot
# Genera una Policy vuota
php artisan make:policy PostPolicy

# Genera con i metodi CRUD legati al modello
php artisan make:policy PostPolicy --model=Post
Ultima modifica il 13 luglio 2026