Flask-Login — Inscription, connexion et déconnexion des utilisateurs
Dans ce chapitre, vous apprendrez à implémenter l'authentification des utilisateurs en session avec Flask-Login, qui est la mesure de sécurité la plus importante dans le développement web avec Flask.
Qu'est-ce que Flask-Login ?
Les requêtes HTTP dans les applications web sont par nature sans état — le serveur ne se souvient pas automatiquement de qui vous êtes.
Flask-LoginGrâce àSessionLe mécanisme maintient l'état de l'utilisateur : après la connexion, le serveur stocke l'ID de l'utilisateur dans la session, et les requêtes suivantes identifient automatiquement l'utilisateur actuel.
Installation et initialisation
(venv) $ pip install flask-login
Exemple
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_login import LoginManager
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///blog.db'
app.config['SECRET_KEY'] = 'your-secret-key-here' # Clé de chiffrement de session (utiliser une variable d'environnement en production)
db = SQLAlchemy(app)
# Initialiser LoginManager
login_manager = LoginManager()
login_manager.init_app(app)
login_manager.login_view = 'auth.login' # Rediriger les utilisateurs non authentifiés vers la page de connexion
login_manager.login_message = 'Veuillez d'abord vous connecter avant d'accéder.'
SECRET_KEYC'est la pierre angulaire du système de sécurité de Flask — le chiffrement de session, les jetons CSRF et les messages flash en dépendent tous. Dans les environnements de production, il doit être lu à partir de variables d'environnement et ne doit pas être codé en dur.
Définir le modèle User
Flask-Login exige que le modèle User implémenteUserMixinplusieurs méthodes fournies.
Exemple
from flask_login import UserMixin
from werkzeug.security import generate_password_hash, check_password_hash
class User(UserMixin, db.Model):
"""Modèle d'utilisateur"""
__tablename__ = 'users'
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(50), unique=True, nullable=False)
email = db.Column(db.String(120), unique=True, nullable=False)
password_hash = db.Column(db.String(256), nullable=False)
def set_password(self, password):
"""Hacher et chiffrer le mot de passe (ne jamais stocker les mots de passe en clair)"""
self.password_hash = generate_password_hash(password)
def check_password(self, password):
"""Vérifier si le mot de passe correspond"""
return check_password_hash(self.password_hash, password)
def __repr__(self):
return f'<User {self.username}>'
# Définir user_loader : Flask-Login utilise cette fonction pour restaurer l'objet utilisateur à partir de la session
@login_manager.user_loader
def load_user(user_id):
return User.query.get(int(user_id))
UserMixin injecte automatiquement les attributs et méthodes suivants dans le modèle User :
| Attributs/Méthodes | Description |
|---|---|
| is_authenticated | Si l'utilisateur est connecté |
| is_active | Si l'utilisateur est actif |
| is_anonymous | S'il s'agit d'un utilisateur anonyme (non connecté) |
| get_id() | Obtenir l'ID de clé primaire de l'utilisateur |
Générer la migration et exécuter :
(venv) $ flask db migrate -m "新增 User 模型" (venv) $ flask db upgrade
Créer un Blueprint d'authentification
Exemple
from flask import Blueprint, render_template, redirect, url_for, request, flash
from flask_login import login_user, logout_user, login_required, current_user
from app.models import User, db
auth_bp = Blueprint('auth', __name__)
@auth_bp.route("/register", methods=['GET', 'POST'])
def register():
"""Inscription de l'utilisateur"""
if current_user.is_authenticated:
return redirect(url_for('main.index'))
if request.method == 'POST':
username = request.form.get('username', '').strip()
email = request.form.get('email', '').strip()
password = request.form.get('password', '')
# Valider les champs obligatoires
if not username or not email or not password:
flash('Tous les champs doivent être remplis.', 'error')
return render_template('register.html')
# Vérifier si le nom d'utilisateur et l'e-mail existent déjà
if User.query.filter_by(username=username).first():
flash('Le nom d’utilisateur est déjà pris.', 'error')
return render_template('register.html')
if User.query.filter_by(email=email).first():
flash('L’adresse e-mail est déjà enregistrée.', 'error')
return render_template('register.html')
# Créer un utilisateur
user = User(username=username, email=email)
user.set_password(password) # Hacher et stocker le mot de passe
db.session.add(user)
db.session.commit()
login_user(user) # Connexion automatique après l'inscription
flash(f'Inscription réussie, bienvenue, {username}!', 'success')
return redirect(url_for('main.index'))
return render_template('register.html')
@auth_bp.route("/login", methods=['GET', 'POST'])
def login():
"""Connexion de l'utilisateur"""
if current_user.is_authenticated:
return redirect(url_for('main.index'))
if request.method == 'POST':
username = request.form.get('username', '').strip()
password = request.form.get('password', '')
# user_loader recherche l'utilisateur dans la base de données
user = User.query.filter_by(username=username).first()
# Vérifier le mot de passe (le mot de passe lui-même ne sera pas enregistré dans les logs)
if user is None or not user.check_password(password):
flash(Nom d'utilisateur ou mot de passe incorrect., 'error')
return render_template('login.html')
login_user(user, remember=request.form.get('remember'))
flash(f'Bon retour, {username} !', 'success')
# Après connexion, rediriger vers la page visitée avant la connexion (le paramètre next dans l'URL)
next_page = request.args.get('next')
return redirect(next_page or url_for('main.index'))
return render_template('login.html')
@auth_bp.route("/logout")
@login_required # Seuls les utilisateurs connectés peuvent se déconnecter
def logout():
logout_user()
flash('Déconnexion sécurisée.', 'info')
return redirect(url_for('main.index'))
Références nécessaires dans la conception de l'application
Exemple
from app.blueprints.auth import auth_bp
app.register_blueprint(auth_bp)
Ne stockez jamais les mots de passe en texte clair !
generate_password_hash()Utilisez l'algorithme PBKDF2 pour effectuer un hachage salé des mots de passe. Même si la base de données est divulguée, les attaquants ne peuvent pas retrouver le mot de passe. Lors de la vérification du mot de passe, utilisezcheck_password_hash()。
Créez les modèles de connexion et d'inscription
Exemple
{% extends 'base.html' %}
{% block title %}Connexion - EXAMPLE Blog{% endblock %}
{% block content %}
<div class="auth-form">
<h2>Se connecter</h2>
<form method="post">
<div class="form-group">
<label>Nom d'utilisateur</label>
<input type="text" name="username" required>
</div>
<div class="form-group">
<label>Mot de passe</label>
<input type="password" name="password" required>
</div>
<div class="form-group">
<label>
<input type="checkbox" name="remember">Se souvenir de moi
</label>
</div>
<button type="submit" class="btn-submit">Se connecter</button>
</forme>
<p class="form-footer">
Pas encore de compte ?<a href="{{ url_for('auth.register') }}">Inscrivez-vous maintenant</a>
</p>
</div>
{% endblock %}
Mettre à jour la barre de navigation pour afficher l'état de connexion
Exemple
<nav>
<a href="/">Accueil</a>
{% if current_user.is_authenticated %}
<span class="user-name">{{ current_user.username }}</span>
<a href="{{ url_for('auth.logout') }}">Déconnexion</a>
{% else %}
<a href="{{ url_for('auth.login') }}">Connexion</a>
<a href="{{ url_for('auth.register') }}">Inscription</a>
{% endif %}
</nav>
current_userC'est un objet proxy automatiquement injecté par Flask-Login, directement utilisable dans tous les templates et fonctions de vue. Lorsqu'il est connecté, c'est un objet User ; lorsqu'il n'est pas connecté, c'est une instance de AnonymousUserMixin.
Résumé du chapitre
Dans ce chapitre, vous avez intégré l'authentification par session de Flask-Login : UserMixin + user_loader ont combiné le modèle utilisateur, login_user/logout_user ont géré la session, werkzeug a haché les mots de passe, @login_required a protégé les vues, et current_user dans les templates a permis de vérifier le statut.
Maintenant, le blog dispose d'un flux complet d'inscription, de connexion et de déconnexion des utilisateurs.
autres extensions