Flask-Login — User registration, login, logout
In this chapter, you will learn to implement session user authentication with Flask-Login, which is the most important security safeguard in Flask web development.
What is Flask-Login?
HTTP requests in web applications are stateless in nature — the server does not automatically remember who you are.
Flask-LoginThroughSessionThe mechanism maintains user state: after login, the server stores the user ID in the Session, and subsequent requests automatically identify the current user.
Installation and Initialization
(venv) $ pip install flask-login
Example
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 encryption key (use environment variable in production)
db = SQLAlchemy(app)
# Initialize LoginManager
login_manager = LoginManager()
login_manager.init_app(app)
login_manager.login_view = 'auth.login' # Redirect unauthenticated users to login page
login_manager.login_message = 'Please log in first before accessing.'
SECRET_KEYIt is the cornerstone of the Flask security system — session encryption, CSRF tokens, and flash messages all depend on it. In production environments, it must be read from environment variables and must not be hard-coded.
Define the User model
Flask-Login requires the User model to implementUserMixinSeveral methods provided.
Example
from flask_login import UserMixin
from werkzeug.security import generate_password_hash, check_password_hash
class User(UserMixin, db.Model):
"""User model"""
__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):
"""Hash and encrypt the password (never store plaintext passwords)"""
self.password_hash = generate_password_hash(password)
def check_password(self, password):
"""Verify whether the password matches"""
return check_password_hash(self.password_hash, password)
def __repr__(self):
return f'<User {self.username}>'
# Define user_loader: Flask-Login uses this function to restore the user object from the session
@login_manager.user_loader
def load_user(user_id):
return User.query.get(int(user_id))
UserMixin automatically injects the following attributes and methods into the User model:
| Attributes/Methods | Description |
|---|---|
| is_authenticated | Whether the user is logged in |
| is_active | Whether the user is active |
| is_anonymous | Whether it is an anonymous user (not logged in) |
| get_id() | Get the user primary key ID |
Generate migration and execute:
(venv) $ flask db migrate -m "新增 User 模型" (venv) $ flask db upgrade
Create an authentication Blueprint
Example
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():
"""User registration"""
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', '')
# Validate required fields
if not username or not email or not password:
flash('All fields must be filled in.', 'error')
return render_template('register.html')
# Check if username and email already exist
if User.query.filter_by(username=username).first():
flash('Username is already taken.', 'error')
return render_template('register.html')
if User.query.filter_by(email=email).first():
flash('Email is already registered.', 'error')
return render_template('register.html')
# Create a user
user = User(username=username, email=email)
user.set_password(password) # Hash and store the password
db.session.add(user)
db.session.commit()
login_user(user) # Automatically log in after registration
flash(f'Registration successful, welcome, {username}!', 'success')
return redirect(url_for('main.index'))
return render_template('register.html')
@auth_bp.route("/login", methods=['GET', 'POST'])
def login():
"""User login"""
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 looks up the user from the database
user = User.query.filter_by(username=username).first()
# Verify the password (the password itself will not be recorded in logs)
if user is None or not user.check_password(password):
flash(Incorrect username or password., 'error')
return render_template('login.html')
login_user(user, remember=request.form.get('remember'))
flash(f'Welcome back, {username}!', 'success')
# After login, redirect to the page visited before login (the next parameter in the 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 # Only logged-in users can log out
def logout():
logout_user()
flash('Logged out securely.', 'info')
return redirect(url_for('main.index'))
References needed in the App design
Example
from app.blueprints.auth import auth_bp
app.register_blueprint(auth_bp)
Never store passwords in plain text!
generate_password_hash()Use the PBKDF2 algorithm to perform salted hashing on passwords. Even if the database is leaked, attackers cannot reverse-engineer the password. When verifying the password, usecheck_password_hash()。
Create login and registration templates
Example
{% extends 'base.html' %}
{% block title %}Login - EXAMPLE Blog{% endblock %}
{% block content %}
<div class="auth-form">
<h2>Log in</h2>
<form method="post">
<div class="form-group">
<label>Username</label>
<input type="text" name="username" required>
</div>
<div class="form-group">
<label>Password</label>
<input type="password" name="password" required>
</div>
<div class="form-group">
<label>
<input type="checkbox" name="remember">Remember me
</label>
</div>
<button type="submit" class="btn-submit">Log in</button>
</form>
<p class="form-footer">
No account yet?<a href="{{ url_for('auth.register') }}">Register now</a>
</p>
</div>
{% endblock %}
Update navigation bar to show login status
Example
<nav>
<a href="/">Home</a>
{% if current_user.is_authenticated %}
<span class="user-name">{{ current_user.username }}</span>
<a href="{{ url_for('auth.logout') }}">Logout</a>
{% else %}
<a href="{{ url_for('auth.login') }}">Log in</a>
<a href="{{ url_for('auth.register') }}">Register</a>
{% endif %}
</nav>
current_userIt is a proxy object automatically injected by Flask-Login, directly usable in all templates and view functions. When logged in, it is a User object; when not logged in, it is an AnonymousUserMixin instance.
Chapter summary
In this chapter, you integrated Flask-Login Session authentication: UserMixin + user_loader combined the user model, login_user/logout_user managed the Session, werkzeug hashed passwords, @login_required protected views, and current_user in templates checked the status.
Now the blog has a complete user registration, login, and logout flow.
other extensions