Flask用户认证:Flask-Login实现权限控制

在Web应用中,用户认证(确认用户身份)和权限控制(根据用户角色限制操作)是核心功能。Flask作为轻量级Web框架,通过Flask-Login扩展可以轻松实现这些功能。本文将用最简单的步骤,带初学者理解如何用Flask-Login实现用户登录、认证状态保持和基础权限控制。

一、准备工作:安装必要库

首先,需要安装Flask和Flask-Login。如果需要存储用户数据,还需安装Flask-SQLAlchemy(数据库ORM)和Werkzeug(处理密码哈希)。打开终端执行:

pip install flask flask-login flask-sqlalchemy werkzeug

二、核心概念:Flask-Login的作用

Flask-Login主要解决两个问题:
1. 会话管理:自动维护用户登录状态(如记住用户、自动登出超时)。
2. 权限验证:通过装饰器控制路由访问权限(如仅登录用户可访问)。

三、步骤1:配置应用与用户模型

1. 初始化应用和数据库

创建app.py,先初始化Flask应用和数据库:

from flask import Flask, render_template, redirect, url_for, request, flash
from flask_login import LoginManager, UserMixin, login_user, login_required, logout_user, current_user
from flask_sqlalchemy import SQLAlchemy
from werkzeug.security import generate_password_hash, check_password_hash

# 初始化Flask应用
app = Flask(__name__)
app.config['SECRET_KEY'] = 'your-secret-key-here'  # 用于会话加密,生产环境需换为随机字符串
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///users.db'  # 配置SQLite数据库
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False  # 关闭不必要的修改跟踪

# 初始化数据库和LoginManager
db = SQLAlchemy(app)
login_manager = LoginManager()
login_manager.init_app(app)
login_manager.login_view = 'login'  # 未登录时跳转的路由(如登录页)

2. 定义用户模型

创建User类,继承UserMixin(自动实现Flask-Login所需的默认方法),并存储用户信息:

class User(UserMixin, db.Model):
    id = db.Column(db.Integer, primary_key=True)  # 用户ID
    username = db.Column(db.String(80), unique=True, nullable=False)  # 用户名
    password_hash = db.Column(db.String(120), nullable=False)  # 密码哈希(非明文)

    # 密码加密方法(存储时用)
    def set_password(self, password):
        self.password_hash = generate_password_hash(password)

    # 密码验证方法(登录时用)
    def check_password(self, password):
        return check_password_hash(self.password_hash, password)

3. 创建数据库表

在终端执行python进入交互模式,创建数据库表:

>>> from app import app, db
>>> with app.app_context():
...     db.create_all()  # 创建所有表结构

四、步骤2:配置用户加载与认证

1. 配置用户加载函数

Flask-Login需要一个函数从会话中加载用户。用@login_manager.user_loader装饰器注册:

@login_manager.user_loader
def load_user(user_id):
    return User.query.get(int(user_id))  # 根据用户ID查询用户对象
  • 作用:用户登录后,Flask-Login会通过这个函数从数据库加载用户信息到会话中。

五、步骤3:实现登录与登出功能

1. 登录页面与表单

创建简单的登录页面(login.html),包含用户名和密码输入框:

<!-- templates/login.html -->
<h1>登录</h1>
{% if error %}
    <p style="color: red;">{{ error }}</p>
{% endif %}
<form method="POST">
    <input type="text" name="username" placeholder="用户名" required><br><br>
    <input type="password" name="password" placeholder="密码" required><br><br>
    <button type="submit">登录</button>
</form>

2. 登录路由

app.py中添加登录逻辑:

@app.route('/login', methods=['GET', 'POST'])
def login():
    if request.method == 'POST':
        username = request.form['username']
        password = request.form['password']
        user = User.query.filter_by(username=username).first()

        # 验证用户是否存在且密码正确
        if user and user.check_password(password):
            login_user(user)  # 保持登录状态(自动生成会话)
            return redirect(url_for('dashboard'))  # 登录成功跳转首页
        else:
            return render_template('login.html', error='用户名或密码错误')

    return render_template('login.html')  # GET请求显示登录表单

3. 登出路由

logout_user()清除会话:

@app.route('/logout')
@login_required  # 仅登录用户可访问登出
def logout():
    logout_user()  # 登出用户,清除会话
    return redirect(url_for('login'))  # 重定向到登录页

六、步骤4:权限控制

1. 保护路由(仅登录用户可访问)

@login_required装饰器限制路由访问:

@app.route('/dashboard')
@login_required  # 未登录用户会被重定向到login_view(即/login)
def dashboard():
    return f"欢迎回来,{current_user.username}!"  # current_user是当前登录用户对象

2. 角色权限控制(进阶)

如果需要更细粒度的权限(如管理员vs普通用户),可在用户模型中添加角色字段:

class User(UserMixin, db.Model):
    # ... 其他字段 ...
    role = db.Column(db.String(20), default='user')  # 角色:user或admin

# 限制管理员路由
@app.route('/admin')
@login_required
def admin_panel():
    if current_user.role != 'admin':  # 检查用户角色
        flash('你没有管理员权限!')
        return redirect(url_for('dashboard'))
    return "管理员后台"

七、关键注意事项

  1. 密码安全:必须用Werkzeuggenerate_password_hash加密存储密码,绝对禁止明文存储
  2. 会话安全SECRET_KEY需随机且复杂,生产环境建议通过环境变量设置。
  3. 用户加载@login_manager.user_loader是核心,必须确保返回正确的用户对象。
  4. 权限验证current_user对象可直接获取用户信息,用于动态控制页面内容。

八、总结

通过Flask-Login,我们实现了:
- 用户登录状态的自动维护(会话管理)。
- 路由权限控制(@login_required)。
- 基础角色权限验证(通过用户角色字段)。

后续可扩展方向:添加“记住我”功能、密码重置、第三方登录(如OAuth)等。

完整代码示例

以下是整合后的app.pylogin.html核心代码:

app.py

from flask import Flask, render_template, redirect, url_for, request, flash
from flask_login import LoginManager, UserMixin, login_user, login_required, logout_user, current_user
from flask_sqlalchemy import SQLAlchemy
from werkzeug.security import generate_password_hash, check_password_hash

app = Flask(__name__)
app.config['SECRET_KEY'] = 'your-secret-key'
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///users.db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False

db = SQLAlchemy(app)
login_manager = LoginManager(app)
login_manager.login_view = 'login'

class User(UserMixin, db.Model):
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(80), unique=True, nullable=False)
    password_hash = db.Column(db.String(120), nullable=False)
    role = db.Column(db.String(20), default='user')

    def set_password(self, password):
        self.password_hash = generate_password_hash(password)

    def check_password(self, password):
        return check_password_hash(self.password_hash, password)

@login_manager.user_loader
def load_user(user_id):
    return User.query.get(int(user_id))

@app.route('/login', methods=['GET', 'POST'])
def login():
    if request.method == 'POST':
        username = request.form['username']
        password = request.form['password']
        user = User.query.filter_by(username=username).first()
        if user and user.check_password(password):
            login_user(user)
            return redirect(url_for('dashboard'))
        else:
            flash('用户名或密码错误')
    return render_template('login.html')

@app.route('/logout')
@login_required
def logout():
    logout_user()
    return redirect(url_for('login'))

@app.route('/dashboard')
@login_required
def dashboard():
    return f"欢迎,{current_user.username}!"

@app.route('/admin')
@login_required
def admin():
    if current_user.role != 'admin':
        flash('无权限访问')
        return redirect(url_for('dashboard'))
    return "管理员页面"

if __name__ == '__main__':
    with app.app_context():
        db.create_all()  # 确保第一次运行时创建表
    app.run(debug=True)

templates/login.html

<!DOCTYPE html>
<html>
<head><title>登录</title></head>
<body>
    <h1>用户登录</h1>
    {% with messages = get_flashed_messages() %}
        {% if messages %}
            <p style="color: red;">{{ messages[0] }}</p>
        {% endif %}
    {% endwith %}
    <form method="POST">
        <input type="text" name="username" placeholder="用户名" required><br><br>
        <input type="password" name="password" placeholder="密码" required><br><br>
        <button type="submit">登录</button>
    </form>
</body>
</html>

运行python app.py,访问http://localhost:5000/login即可测试登录功能!

小夜