Flask-Login — Benutzerregistrierung, Anmeldung, Abmeldung

In diesem Kapitel lernst du, die Benutzerauthentifizierung per Sitzung mit Flask-Login zu implementieren, die die wichtigste Sicherheitsmaßnahme in der Flask-Webentwicklung ist.


Was ist Flask-Login?

HTTP-Anfragen in Webanwendungen sind von Natur aus zustandslos — der Server merkt sich nicht automatisch, wer Sie sind.

Flask-LoginDurchSessionDer Mechanismus hält den Benutzerstatus aufrecht: Nach dem Login speichert der Server die Benutzer-ID in der Session, und nachfolgende Anfragen identifizieren automatisch den aktuellen Benutzer.


Installation und Initialisierung

(venv) $ pip install flask-login

Beispiel

# Dateipfad: app.py
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'   # Session-Verschlüsselungsschlüssel (in Produktion Umgebungsvariable verwenden)

db = SQLAlchemy(app)

# LoginManager initialisieren
login_manager = LoginManager()
login_manager.init_app(app)
login_manager.login_view = 'auth.login'     # Nicht authentifizierte Benutzer zur Anmeldeseite umleiten
login_manager.login_message = 'Bitte melden Sie sich zuerst an, bevor Sie zugreifen.'

SECRET_KEYEs ist der Grundpfeiler des Flask-Sicherheitssystems — Session-Verschlüsselung, CSRF-Tokens und Flash-Nachrichten hängen alle davon ab. In Produktionsumgebungen muss es aus Umgebungsvariablen gelesen werden und darf nicht hartcodiert sein.


Das User-Modell definieren

Flask-Login erfordert, dass das User-ModellUserMixinmehrere bereitgestellte Methoden implementiert.

Beispiel

# Dateipfad: models.py, neue Ergänzungen
from flask_login import UserMixin
from werkzeug.security import generate_password_hash, check_password_hash

class User(UserMixin, db.Model):
    """Benutzermodell"""
    __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):
        """ und verschlüsseln Sie das Passwort (niemals Klartextpasswörter speichern)"""
        self.password_hash = generate_password_hash(password)

    def check_password(self, password):
        """Überprüft, ob das Passwort übereinstimmt"""
        return check_password_hash(self.password_hash, password)

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

# user_loader definieren: Flask-Login verwendet diese Funktion, um das User-Objekt aus der Session wiederherzustellen
@login_manager.user_loader
def load_user(user_id):
    return User.query.get(int(user_id))

UserMixin fügt automatisch die folgenden Attribute und Methoden in das User-Modell ein:

Attribute/MethodenBeschreibung
is_authenticatedOb der Benutzer angemeldet ist
is_activeOb der Benutzer aktiv ist
is_anonymousOb es sich um einen anonymen Benutzer handelt (nicht angemeldet)
get_id()Die Primärschlüssel-ID des Benutzers abrufen

Migration generieren und ausführen:

(venv) $ flask db migrate -m "新增 User 模型"
(venv) $ flask db upgrade

Ein Authentifizierungs-Blueprint erstellen

Beispiel

# Dateipfad: app/blueprints/auth.py
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():
    """Benutzerregistrierung"""
    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', '')

        # Erforderliche Felder validieren
        if not username or not email or not password:
            flash('Alle Felder müssen ausgefüllt werden.', 'error')
            return render_template('register.html')

        # Prüfen, ob Benutzername und E-Mail bereits existieren
        if User.query.filter_by(username=username).first():
            flash('Benutzername ist bereits vergeben.', 'error')
            return render_template('register.html')
        if User.query.filter_by(email=email).first():
            flash('E-Mail ist bereits registriert.', 'error')
            return render_template('register.html')

        # Benutzer erstellen
        user = User(username=username, email=email)
        user.set_password(password)    # Passwort hashen und speichern
        db.session.add(user)
        db.session.commit()

        login_user(user)               # Nach der Registrierung automatisch anmelden
        flash(f'Registrierung erfolgreich, willkommen, {username}!', 'success')
        return redirect(url_for('main.index'))

    return render_template('register.html')


@auth_bp.route("/login", methods=['GET', 'POST'])
def login():
    """Benutzeranmeldung"""
    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 sucht den Benutzer in der Datenbank
        user = User.query.filter_by(username=username).first()

        # Passwort überprüfen (das Passwort selbst wird nicht in Logs aufgezeichnet)
        if user is None or not user.check_password(password):
            flash(Falscher Benutzername oder falsches Passwort., 'error')
            return render_template('login.html')

        login_user(user, remember=request.form.get('remember'))
        flash(f'Willkommen zurück, {username}!', 'success')

        # Nach dem Login zur vor dem Login besuchten Seite weiterleiten (der Parameter next in der 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       # Nur angemeldete Benutzer können sich abmelden
def logout():
    logout_user()
    flash('Sicher abgemeldet.', 'info')
    return redirect(url_for('main.index'))

Für das App-Design benötigte Referenzen

Beispiel

# Dateipfad: auth-Blueprint in app.py registrieren
from app.blueprints.auth import auth_bp
app.register_blueprint(auth_bp)

Passwörter niemals im Klartext speichern!generate_password_hash()Verwende den PBKDF2-Algorithmus, um Passwörter mit Salt zu hashen. Selbst wenn die Datenbank geleakt wird, können Angreifer das Passwort nicht zurückrechnen. Verwende beim Verifizieren des Passwortscheck_password_hash()。


Login- und Registrierungs-Templates erstellen

Beispiel

<!-- Dateipfad: app/templates/login.html -->
{% extends 'base.html' %}

{% block title %}Anmeldung - EXAMPLE Blog{% endblock %}

{% block content %}
<div class="auth-form">
    <h2>Anmelden</h2>
    <form method="post">
        <div class="form-group">
            <label>Benutzername</label>
            <input type="text" name="username" required>
        </div>
        <div class="form-group">
            <label>Passwort</label>
            <input type="password" name="password" required>
        </div>
        <div class="form-group">
            <label>
                <input type="checkbox" name="remember">Angemeldet bleiben
            </label>
        </div>
        <button type="submit" class="btn-submit">Anmelden</button>
    </Form>
    <p class="form-footer">
Noch kein Konto?<a href="{{ url_for('auth.register') }}">Jetzt registrieren</a>
    </p>
</div>
{% endblock %}

Navigationsleiste aktualisieren, um den Anmeldestatus anzuzeigen

Beispiel

<!-- Dateipfad: Navigationsleisten-Abschnitt in app/templates/base.html -->
<nav>
    <a href="/">Startseite</a>
    {% if current_user.is_authenticated %}
        <span class="user-name">{{ current_user.username }}</span>
        <a href="{{ url_for('auth.logout') }}">Abmelden</a>
    {% else %}
        <a href="{{ url_for('auth.login') }}">Anmelden</a>
        <a href="{{ url_for('auth.register') }}">Registrieren</a>
    {% endif %}
</nav>

current_userEs ist ein Proxy-Objekt, das automatisch von Flask-Login injiziert wird und direkt in allen Templates und View-Funktionen verwendet werden kann. Wenn man angemeldet ist, ist es ein User-Objekt; wenn man nicht angemeldet ist, ist es eine AnonymousUserMixin-Instanz.


Kapitelzusammenfassung

In diesem Kapitel haben Sie die Flask-Login-Session-Authentifizierung integriert: UserMixin + user_loader banden das Benutzermodell ein, login_user/logout_user verwalteten die Session, werkzeug hashte Passwörter, @login_required schützte Views, und current_user in Templates prüfte den Status.

Jetzt hat der Blog einen vollständigen Ablauf für Benutzerregistrierung, Anmeldung und Abmeldung.

Weitere Erweiterungen