项目结构
首先创建以下文件结构:
flask_login_use/
├── app.py
├── models.py
├── requirements.txt
└── templates/
├── base.html
├── index.html
├── login.html
├── register.html
└── profile.html
1. requirements.txt
Flask==2.3.3
Flask-Login==0.6.3
Flask-WTF==1.1.1
WTForms==3.0.1
Werkzeug==2.3.7
email_validator
2. models.py - 用户模型
from flask_login import UserMixin
from werkzeug.security import generate_password_hash, check_password_hash
class User(UserMixin):
def __init__(self, id, username, email, password_hash):
self.id = id
self.username = username
self.email = email
self.password_hash = password_hash
def check_password(self, password):
return check_password_hash(self.password_hash, password)
@staticmethod
def create_password_hash(password):
return generate_password_hash(password)
# 模拟数据库存储
users_db = {}
next_user_id = 1
def get_user(user_id):
return users_db.get(int(user_id))
def get_user_by_username(username):
for user in users_db.values():
if user.username == username:
return user
return None
def create_user(username, email, password):
global next_user_id
password_hash = User.create_password_hash(password)
user = User(next_user_id, username, email, password_hash)
users_db[next_user_id] = user
next_user_id += 1
return user
3. app.py - 主应用文件
from flask import Flask, render_template, request, redirect, url_for, flash
from flask_login import LoginManager, login_user, logout_user, login_required, current_user
from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, SubmitField
from wtforms.validators import DataRequired, Email, Length, EqualTo
from models import User, get_user, get_user_by_username, create_user
app = Flask(__name__)
app.config['SECRET_KEY'] = 'your-secret-key-here' # 在生产环境中使用更安全的密钥
# 初始化Flask-Login
login_manager = LoginManager()
login_manager.init_app(app)
login_manager.login_view = 'login'
login_manager.login_message = '请先登录以访问此页面。'
login_manager.login_message_category = 'info'
@login_manager.user_loader
def load_user(user_id):
return get_user(user_id)
# 表单类
class LoginForm(FlaskForm):
username = StringField('用户名', validators=[DataRequired()])
password = PasswordField('密码', validators=[DataRequired()])
submit = SubmitField('登录')
class RegisterForm(FlaskForm):
username = StringField('用户名', validators=[
DataRequired(),
Length(min=4, max=20, message='用户名长度必须在4-20个字符之间')
])
email = StringField('邮箱', validators=[DataRequired(), Email(message='请输入有效的邮箱地址')])
password = PasswordField('密码', validators=[
DataRequired(),
Length(min=6, message='密码长度至少6个字符')
])
password2 = PasswordField('确认密码', validators=[
DataRequired(),
EqualTo('password', message='两次输入的密码不一致')
])
submit = SubmitField('注册')
# 路由
@app.route('/')
def index():
return render_template('index.html')
@app.route('/login', methods=['GET', 'POST'])
def login():
if current_user.is_authenticated:
return redirect(url_for('index'))
form = LoginForm()
if form.validate_on_submit():
user = get_user_by_username(form.username.data)
if user and user.check_password(form.password.data):
login_user(user)
flash('登录成功!', 'success')
# 获取用户尝试访问的页面
next_page = request.args.get('next')
return redirect(next_page) if next_page else redirect(url_for('index'))
else:
flash('用户名或密码错误', 'danger')
return render_template('login.html', form=form)
@app.route('/register', methods=['GET', 'POST'])
def register():
if current_user.is_authenticated:
return redirect(url_for('index'))
form = RegisterForm()
if form.validate_on_submit():
# 检查用户名是否已存在
if get_user_by_username(form.username.data):
flash('用户名已存在', 'danger')
else:
# 创建新用户
user = create_user(
username=form.username.data,
email=form.email.data,
password=form.password.data
)
flash('注册成功!请登录。', 'success')
return redirect(url_for('login'))
return render_template('register.html', form=form)
@app.route('/logout')
@login_required
def logout():
logout_user()
flash('您已成功登出', 'info')
return redirect(url_for('index'))
@app.route('/profile')
@login_required
def profile():
return render_template('profile.html')
@app.route('/protected')
@login_required
def protected():
return f'<h1>受保护的页面</h1><p>你好,{current_user.username}!这是一个需要登录才能访问的页面。</p><a href="{url_for("index")}">返回首页</a>'
if __name__ == '__main__':
app.run(debug=True)
4. 模板文件
base.html - 基础模板
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% block title %}Flask-Login 示例{% endblock %}</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
<div class="container">
<a class="navbar-brand" href="{{ url_for('index') }}">Flask-Login 示例</a>
<div class="navbar-nav ms-auto">
{% if current_user.is_authenticated %}
<a class="nav-link" href="{{ url_for('profile') }}">个人资料</a>
<a class="nav-link" href="{{ url_for('protected') }}">受保护页面</a>
<a class="nav-link" href="{{ url_for('logout') }}">登出 ({{ current_user.username }})</a>
{% else %}
<a class="nav-link" href="{{ url_for('login') }}">登录</a>
<a class="nav-link" href="{{ url_for('register') }}">注册</a>
{% endif %}
</div>
</div>
</nav>
<div class="container mt-4">
{% with messages = get_flashed_messages(with_categories=true) %}
{% if messages %}
{% for category, message in messages %}
<div class="alert alert-{{ 'danger' if category == 'error' else category }} alert-dismissible fade show" role="alert">
{{ message }}
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
</div>
{% endfor %}
{% endif %}
{% endwith %}
{% block content %}{% endblock %}
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>
index.html - 首页
{% extends "base.html" %}
{% block content %}
<div class="row">
<div class="col-md-8 mx-auto">
<div class="jumbotron bg-light p-5 rounded">
<h1 class="display-4">欢迎使用 Flask-Login 示例</h1>
<p class="lead">这是一个演示 Flask-Login 功能的完整示例应用。</p>
{% if current_user.is_authenticated %}
<h4>你好,{{ current_user.username }}!</h4>
<p>你已经成功登录。你可以:</p>
<ul>
<li><a href="{{ url_for('profile') }}">查看个人资料</a></li>
<li><a href="{{ url_for('protected') }}">访问受保护的页面</a></li>
<li><a href="{{ url_for('logout') }}">登出</a></li>
</ul>
{% else %}
<p>请先登录或注册以体验完整功能。</p>
<a class="btn btn-primary btn-lg" href="{{ url_for('login') }}" role="button">登录</a>
<a class="btn btn-secondary btn-lg" href="{{ url_for('register') }}" role="button">注册</a>
{% endif %}
</div>
</div>
</div>
{% endblock %}
login.html - 登录页面
{% extends "base.html" %}
{% block title %}登录 - Flask-Login 示例{% endblock %}
{% block content %}
<div class="row">
<div class="col-md-6 mx-auto">
<div class="card">
<div class="card-header">
<h3>登录</h3>
</div>
<div class="card-body">
<form method="POST">
{{ form.hidden_tag() }}
<div class="mb-3">
{{ form.username.label(class="form-label") }}
{{ form.username(class="form-control") }}
{% if form.username.errors %}
<div class="text-danger">
{% for error in form.username.errors %}
<small>{{ error }}</small>
{% endfor %}
</div>
{% endif %}
</div>
<div class="mb-3">
{{ form.password.label(class="form-label") }}
{{ form.password(class="form-control") }}
{% if form.password.errors %}
<div class="text-danger">
{% for error in form.password.errors %}
<small>{{ error }}</small>
{% endfor %}
</div>
{% endif %}
</div>
<div class="d-grid">
{{ form.submit(class="btn btn-primary") }}
</div>
</form>
<div class="text-center mt-3">
<p>还没有账户? <a href="{{ url_for('register') }}">立即注册</a></p>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
register.html - 注册页面
{% extends "base.html" %}
{% block title %}注册 - Flask-Login 示例{% endblock %}
{% block content %}
<div class="row">
<div class="col-md-6 mx-auto">
<div class="card">
<div class="card-header">
<h3>注册</h3>
</div>
<div class="card-body">
<form method="POST">
{{ form.hidden_tag() }}
<div class="mb-3">
{{ form.username.label(class="form-label") }}
{{ form.username(class="form-control") }}
{% if form.username.errors %}
<div class="text-danger">
{% for error in form.username.errors %}
<small>{{ error }}</small>
{% endfor %}
</div>
{% endif %}
</div>
<div class="mb-3">
{{ form.email.label(class="form-label") }}
{{ form.email(class="form-control") }}
{% if form.email.errors %}
<div class="text-danger">
{% for error in form.email.errors %}
<small>{{ error }}</small>
{% endfor %}
</div>
{% endif %}
</div>
<div class="mb-3">
{{ form.password.label(class="form-label") }}
{{ form.password(class="form-control") }}
{% if form.password.errors %}
<div class="text-danger">
{% for error in form.password.errors %}
<small>{{ error }}</small>
{% endfor %}
</div>
{% endif %}
</div>
<div class="mb-3">
{{ form.password2.label(class="form-label") }}
{{ form.password2(class="form-control") }}
{% if form.password2.errors %}
<div class="text-danger">
{% for error in form.password2.errors %}
<small>{{ error }}</small>
{% endfor %}
</div>
{% endif %}
</div>
<div class="d-grid">
{{ form.submit(class="btn btn-success") }}
</div>
</form>
<div class="text-center mt-3">
<p>已有账户? <a href="{{ url_for('login') }}">立即登录</a></p>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
profile.html - 个人资料页面
{% extends "base.html" %}
{% block title %}个人资料 - Flask-Login 示例{% endblock %}
{% block content %}
<div class="row">
<div class="col-md-8 mx-auto">
<div class="card">
<div class="card-header">
<h3>个人资料</h3>
</div>
<div class="card-body">
<table class="table">
<tr>
<th>用户ID:</th>
<td>{{ current_user.id }}</td>
</tr>
<tr>
<th>用户名:</th>
<td>{{ current_user.username }}</td>
</tr>
<tr>
<th>邮箱:</th>
<td>{{ current_user.email }}</td>
</tr>
<tr>
<th>登录状态:</th>
<td>
{% if current_user.is_authenticated %}
<span class="badge bg-success">已登录</span>
{% else %}
<span class="badge bg-danger">未登录</span>
{% endif %}
</td>
</tr>
</table>
<div class="mt-3">
<a href="{{ url_for('index') }}" class="btn btn-primary">返回首页</a>
<a href="{{ url_for('logout') }}" class="btn btn-danger">登出</a>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
运行应用
- 安装依赖:
pip install -r requirements.txt
- 运行应用:
python app.py
- 在浏览器中访问
http://localhost:5000
或者用uv来控制项目:
页面测试:
❤️➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖❤️
❤️➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖❤️
❤️➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖❤️
❤️➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖❤️
❤️➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖❤️