Salta al contenuto principale

Come implementare l'autenticazione con JWT in un'applicazione Flask

Profile picture for user luca77king

L'autenticazione è un componente cruciale per qualsiasi applicazione web moderna. Quando si sviluppa un'API RESTful, una delle tecniche più popolari ed efficaci per gestire l'autenticazione degli utenti è l'utilizzo di JSON Web Tokens (JWT). Questo approccio non solo semplifica la gestione delle sessioni, ma consente anche di costruire applicazioni scalabili e sicure, in cui non è necessario mantenere lo stato lato server. In questo articolo, esploreremo come implementare un sistema di autenticazione basato su JWT in un'applicazione web realizzata con Flask.

Un JSON Web Token (JWT) è un metodo di rappresentazione sicura e compatta delle informazioni, utile per l'autenticazione tra il client e il server. La struttura di un JWT è composta da tre sezioni: l'intestazione (header), il payload e la firma. L'intestazione contiene informazioni sul tipo di token e sull'algoritmo di firma utilizzato. Il payload contiene i dati effettivi che vogliamo trasmettere, come l'ID dell'utente. La firma serve per garantire che il token non venga alterato durante il suo percorso. La grande vantaggio di JWT risiede nel fatto che sono auto-contenuti, il che significa che tutte le informazioni necessarie per l'autenticazione sono incluse nel token stesso, evitando la necessità di memorizzare sessioni sul server.

Creazione dell'applicazione Flask

Per cominciare, installiamo Flask e altre librerie necessarie per lavorare con JWT, come pyjwt e flask_sqlalchemy. La libreria flask_sqlalchemy ci permette di gestire facilmente il database, mentre pyjwt sarà utilizzata per generare e validare i JWT.

Per installare le librerie, esegui il comando:

pip install Flask pyjwt flask_sqlalchemy

Iniziamo a configurare la nostra applicazione Flask. Creiamo un file app.py e definiremo una semplice applicazione Flask con un modello di database per gli utenti. Ecco come possiamo fare:

from flask import Flask, request, jsonify
import jwt
import datetime
from functools import wraps
from flask_sqlalchemy import SQLAlchemy

app = Flask(__name__)
app.config['SECRET_KEY'] = 'your_secret_key'  # Cambia con una chiave segreta
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///users.db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False

db = SQLAlchemy(app)

# Modello User per la nostra app
class User(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(50), unique=True, nullable=False)
    password = db.Column(db.String(50), nullable=False)

    def __repr__(self):
        return f"User('{self.username}')"

# Crea il database
with app.app_context():
    db.create_all()

if __name__ == "__main__":
    app.run(debug=True)

Abbiamo configurato un'applicazione di base in Flask con un database SQLite. Il modello User è stato definito per memorizzare gli utenti con un campo username e password. La chiave segreta SECRET_KEY è utilizzata per firmare il token JWT, ed è importante cambiarla con una chiave unica e sicura per la tua applicazione.

Registrazione di un nuovo utente

La registrazione di un nuovo utente è il primo passo verso l'autenticazione. Creiamo una route /register che accetta una richiesta POST per registrare un utente. Il nome utente e la password vengono presi dal corpo della richiesta, quindi verifichiamo se l'utente esiste già nel database. Se l'utente non esiste, lo creiamo e memorizziamo nel database. Ecco come:

@app.route('/register', methods=['POST'])
def register():
    data = request.get_json()

    username = data.get('username')
    password = data.get('password')

    if not username or not password:
        return jsonify({'message': 'Username and password are required'}), 400

    # Verifica se l'utente esiste già
    if User.query.filter_by(username=username).first():
        return jsonify({'message': 'User already exists'}), 400

    # Crea un nuovo utente
    new_user = User(username=username, password=password)
    db.session.add(new_user)
    db.session.commit()

    return jsonify({'message': 'User created successfully'}), 201

In questo codice, la funzione register esegue le seguenti operazioni:

  1. Estrae i dati dal corpo della richiesta.
  2. Verifica se il nome utente e la password sono forniti.
  3. Controlla se l'utente esiste già nel database.
  4. Se l'utente non esiste, viene creato e salvato nel database.

Autenticazione con JWT

Ora che abbiamo un sistema di registrazione, possiamo implementare il login degli utenti. La route /login restituirà un JWT quando le credenziali dell'utente sono corrette. Questo è fondamentale perché il JWT rappresenta l'autenticazione dell'utente e sarà utilizzato per accedere a endpoint protetti. Quando l'utente inserisce il nome utente e la password, Flask verifica se queste corrispondono ai dati nel database. Se sono corretti, viene generato un JWT che contiene l'ID dell'utente e la scadenza del token. Ecco come implementare il login:

@app.route('/login', methods=['POST'])
def login():
    data = request.get_json()

    username = data.get('username')
    password = data.get('password')

    if not username or not password:
        return jsonify({'message': 'Username and password are required'}), 400

    user = User.query.filter_by(username=username).first()

    # Verifica se l'utente esiste e la password è corretta
    if not user or user.password != password:
        return jsonify({'message': 'Invalid credentials'}), 401

    # Creazione del JWT
    token = jwt.encode({
        'user_id': user.id,
        'exp': datetime.datetime.utcnow() + datetime.timedelta(hours=1)
    }, app.config['SECRET_KEY'], algorithm='HS256')

    return jsonify({'token': token}), 200

In questa funzione:

  1. Controlliamo se l'utente esiste e se la password è corretta.
  2. Se le credenziali sono valide, generiamo un token JWT.
  3. Il token contiene l'ID dell'utente e una scadenza di un'ora. La scadenza è importante per limitare la durata di validità del token.

Proteggere gli endpoint con JWT

Una delle caratteristiche principali di JWT è che possiamo usarlo per proteggere gli endpoint della nostra applicazione. Creiamo un decoratore token_required, che verificherà che ogni richiesta a un endpoint protetto contenga un token valido. Se il token non è presente o è invalido, verrà restituito un errore. Ecco come:

def token_required(f):
    @wraps(f)
    def decorated_function(*args, **kwargs):
        token = request.headers.get('Authorization')

        if not token:
            return jsonify({'message': 'Token is missing!'}), 403

        try:
            data = jwt.decode(token, app.config['SECRET_KEY'], algorithms=['HS256'])
            current_user = User.query.get(data['user_id'])
        except Exception as e:
            return jsonify({'message': 'Token is invalid!'}), 403

        return f(current_user, *args, **kwargs)

    return decorated_function

Abbiamo definito il decoratore token_required che estrae il token dall'intestazione Authorization della richiesta, lo decodifica e verifica la sua validità. Se il token è valido, prosegue con l'esecuzione della funzione, passando l'utente corrente come parametro. Se il token è mancante o invalido, viene restituito un errore.

Ora, possiamo applicare questo decoratore a qualsiasi endpoint che desideriamo proteggere, come ad esempio una route che restituisce i dettagli dell'utente:

@app.route('/profile', methods=['GET'])
@token_required
def profile(current_user):
    return jsonify({'username': current_user.username}), 200

In questa route, l'utente dovrà fornire un token valido per accedere alle informazioni del proprio profilo.

Conclusione

Abbiamo visto come implementare un sistema di autenticazione sicuro con JWT in un'applicazione Flask. L'approccio basato su JWT è particolarmente utile per le applicazioni moderne che richiedono una gestione dell'autenticazione senza sessione, poiché consente di gestire l'accesso in modo stateless, senza la necessità di memorizzare sessioni sul server. Inoltre, il sistema di scadenza del token garantisce una maggiore sicurezza. Con la protezione degli endpoint tramite il decoratore token_required, l'applicazione è ora pronta per gestire in modo sicuro gli utenti autenticati.

Faqs

Che cos'è un JSON Web Token (JWT)?
Un JSON Web Token (JWT) è un metodo di rappresentazione sicura e compatta delle informazioni, utile per l'autenticazione tra il client e il server. La struttura di un JWT è composta da tre sezioni: l'intestazione (header), il payload e la firma.
A cosa serve l'autenticazione in un'applicazione web?
L'autenticazione è un componente cruciale per qualsiasi applicazione web moderna. Serve a gestire l'accesso degli utenti e garantire che le informazioni siano fruibili solo da coloro che hanno il permesso di vederle.
Quali sono i vantaggi dell'utilizzo di JSON Web Tokens?
I vantaggi dell'utilizzo di JSON Web Tokens includono la semplificazione della gestione delle sessioni, la costruzione di applicazioni scalabili e sicure, e la possibilità di evitare la necessità di mantenere lo stato lato server.
Cosa rappresenta la sezione 'payload' di un JSON Web Token?
La sezione 'payload' di un JSON Web Token contiene i dati effettivi che vogliamo trasmettere, come l'ID dell'utente.
A che serve la firma in un JSON Web Token?
La firma in un JSON Web Token serve per garantire che il token non venga alterato durante il suo percorso.
Che cos'è Flask?
Flask è un microframework web scritto in Python. Viene utilizzato per creare facilmente applicazioni web e API RESTful.
A cosa servono le librerie pyjwt e flask_sqlalchemy?
Le librerie pyjwt e flask_sqlalchemy sono utilizzate per lavorare con JSON Web Tokens e per gestire facilmente il database in un'applicazione Flask, rispettivamente.