#Flask-Login 实战:用户登录、登出与 Session 管理
#1. Flask-Login 概述
#1.1 核心功能
Flask-Login 提供:
1. 用户登录 / 登出
2. Session 管理(Cookie)
3. 登录保护(login_required)
4. 用户加载回调(user_loader)
5. 当前用户访问(current_user)#1.2 安装
pip install flask-login#2. 配置
#2.1 初始化
# app/extensions.py
from flask_login import LoginManager
login_manager = LoginManager()
# 登录视图(未登录时重定向到这里)
login_manager.login_view = "auth.login"
login_manager.login_message = "请先登录后再访问"
login_manager.login_message_category = "warning"#2.2 应用工厂中初始化
# app/__init__.py
from app.extensions import db, login_manager
def create_app():
app = Flask(__name__)
# ...其他配置...
db.init_app(app)
login_manager.init_app(app)
# 用户加载回调(必须!)
@login_manager.user_loader
def load_user(user_id):
from app.models import User
return db.session.get(User, int(user_id))
return app#2.3 用户模型必须实现的方法
# app/models/user.py
from flask_login import UserMixin
from app.extensions import db
class User(UserMixin, db.Model):
__tablename__ = "users"
id = db.Column(db.Integer, primary_key=True)
email = db.Column(db.String(120), unique=True, nullable=False)
username = db.Column(db.String(50), unique=True)
password_hash = db.Column(db.String(256), nullable=False)
is_active = db.Column(db.Boolean, default=True)
created_at = db.Column(db.DateTime, default=datetime.utcnow)
# Flask-Login 必需的 4 个属性/方法(UserMixin 已实现大部分)
# is_authenticated: 是否已登录
# is_active: 账户是否激活
# is_anonymous: 是否是匿名用户
# get_id(): 获取用户唯一标识符
def __repr__(self):
return f"<User {self.email}>"#3. 登录与登出路由
#3.1 登录路由
# app/routes/auth.py
from flask import Blueprint, render_template, redirect, url_for, flash, request
from flask_login import login_user, current_user
from werkzeug.security import check_password_hash
from app.extensions import db
from app.models import User
from app.forms import LoginForm
auth_bp = Blueprint("auth", __name__, url_prefix="/auth")
@auth_bp.route("/login", methods=["GET", "POST"])
def login():
# 已登录用户访问登录页 → 跳转到首页
if current_user.is_authenticated:
return redirect(url_for("main.index"))
form = LoginForm()
if form.validate_on_submit():
user = User.query.filter_by(email=form.email.data).first()
if user and check_password_hash(user.password_hash, form.password.data):
if not user.is_active:
flash("账户已被禁用,请联系管理员。", "danger")
return render_template("auth/login.html", form=form)
# 登录成功
login_user(user, remember=form.remember_me.data)
# 记住来源页面(登录后跳转回上一页)
next_page = request.args.get("next")
if next_page and next_page.startswith("/"):
return redirect(next_page)
return redirect(url_for("main.index"))
else:
flash("邮箱或密码错误", "danger")
return render_template("auth/login.html", form=form)#3.2 登出路由
# app/routes/auth.py
from flask_login import logout_user, login_required
@auth_bp.route("/logout")
@login_required
def logout():
logout_user() # 清除 session
flash("已安全退出登录。", "info")
return redirect(url_for("auth.login"))#4. 登录保护
#4.1 login_required 装饰器
from flask_login import login_required
@auth_bp.route("/profile")
@login_required
def profile():
return render_template("auth/profile.html", user=current_user)
@auth_bp.route("/settings", methods=["GET", "POST"])
@login_required
def settings():
form = SettingsForm()
if form.validate_on_submit():
current_user.bio = form.bio.data
db.session.commit()
flash("资料已更新!", "success")
return redirect(url_for("auth.profile"))
return render_template("auth/settings.html", form=form)#4.2 局部登录保护
# 仅 POST 请求需要登录(GET 公开,POST 受保护)
@app.route("/comment", methods=["POST"])
@login_required
def add_comment():
...#4.3 自定义权限装饰器
# app/decorators.py
from functools import wraps
from flask import abort
from flask_login import current_user
def admin_required(f):
"""仅管理员可访问"""
@wraps(f)
def decorated_function(*args, **kwargs):
if not current_user.is_authenticated or not current_user.is_admin:
abort(403) # Forbidden
return f(*args, **kwargs)
return decorated_function
def editor_required(f):
"""编辑者或管理员可访问"""
@wraps(f)
def decorated_function(*args, **kwargs):
if not current_user.is_authenticated:
abort(401)
if not (current_user.is_admin or current_user.role == "editor"):
abort(403)
return f(*args, **kwargs)
return decorated_function
# 使用
@auth_bp.route("/admin")
@admin_required
def admin_panel():
return "管理后台"#5. Session 配置
# app/__init__.py
app.config["SECRET_KEY"] = "your-secret-key-change-in-production"
app.config["REMEMBER_COOKIE_DURATION"] = timedelta(days=14) # "记住我"Cookie 有效期
app.config["SESSION_COOKIE_SECURE"] = True # 仅 HTTPS 传输(生产)
app.config["SESSION_COOKIE_HTTPONLY"] = True # JS 无法读取 Cookie
app.config["SESSION_COOKIE_SAMESITE"] = "Lax" # CSRF 防护#6. 模板中的 current_user
<!-- templates/base.html -->
<!-- 导航栏根据登录状态显示不同内容 -->
<nav>
<a href="{{ url_for('index') }}">首页</a>
{% if current_user.is_authenticated %}
<a href="{{ url_for('profile') }}">{{ current_user.username }}</a>
{% if current_user.is_admin %}
<a href="{{ url_for('admin.index') }}">管理后台</a>
{% endif %}
<a href="{{ url_for('logout') }}">退出</a>
{% else %}
<a href="{{ url_for('login') }}">登录</a>
<a href="{{ url_for('register') }}">注册</a>
{% endif %}
</nav>#7. 小结
# Flask-Login 速查
# 初始化
login_manager.login_view = "auth.login"
# 用户加载
@login_manager.user_loader
def load_user(user_id):
return db.session.get(User, int(user_id))
# 用户模型继承
class User(UserMixin, db.Model): ...
# 登录登出
login_user(user, remember=False)
logout_user()
# 当前用户
current_user.is_authenticated # 是否登录
current_user.id # 用户 ID
# 保护路由
@login_required
def protected(): ...
# 登出后重定向(自动由 login_manager 处理)💡 安全提示:
SECRET_KEY必须足够随机且保密,建议使用secrets.token_hex(32)生成并存储在环境变量中,永不上传代码仓库。
🔗 扩展阅读

