在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 "管理员后台"
七、关键注意事项¶
- 密码安全:必须用
Werkzeug的generate_password_hash加密存储密码,绝对禁止明文存储! - 会话安全:
SECRET_KEY需随机且复杂,生产环境建议通过环境变量设置。 - 用户加载:
@login_manager.user_loader是核心,必须确保返回正确的用户对象。 - 权限验证:
current_user对象可直接获取用户信息,用于动态控制页面内容。
八、总结¶
通过Flask-Login,我们实现了:
- 用户登录状态的自动维护(会话管理)。
- 路由权限控制(@login_required)。
- 基础角色权限验证(通过用户角色字段)。
后续可扩展方向:添加“记住我”功能、密码重置、第三方登录(如OAuth)等。
完整代码示例¶
以下是整合后的app.py和login.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即可测试登录功能!