【Flask】Flask用户登录系统(PostgreSQL + 模块化架构)

【Flask】Flask用户登录系统(PostgreSQL + 模块化架构)

  • Flask用户登录系统(PostgreSQL + 模块化架构)
    • 项目结构
    • 代码实现
      • 1. 配置文件 (config.py)
      • 2. 应用工厂 (app/__init__.py)
      • 3. 用户模型 (app/models.py)
      • 4. 认证蓝图 (app/auth)
        • 表单 (app/auth/forms.py)
        • 路由 (app/auth/routes.py)
      • 5. 命令行工具 (app/commands.py)
      • 6. 模板文件 (app/templates/auth)
        • 登录页面 (login.html)
        • 注册页面 (register.html)
      • 7. 主应用文件 (run.py)
    • 安装和运行步骤
      • 1. 创建虚拟环境并安装依赖
      • 2. 创建PostgreSQL数据库
      • 3. 配置环境变量
      • 4. 初始化数据库
      • 5. 运行应用
    • 功能说明
    • 使用说明


Flask用户登录系统(PostgreSQL + 模块化架构)

Flask写一个用户登录的程序,需要用Postgresql数据库来存储数据,用命令来初始化数据库,并刷基础数据,采用分模块的思想来做

下面我将实现一个完整的Flask用户登录系统,采用模块化架构并使用PostgreSQL作为数据库。系统包含以下组件:

  • 数据库模型(models)
  • 认证蓝图(auth)
  • 命令行初始化工具
  • 配置文件管理
  • 密码安全处理

项目结构

flask_login_app/
├── app/
│   ├── __init__.py
│   ├── auth/
│   │   ├── __init__.py
│   │   ├── routes.py
│   │   └── forms.py
│   ├── models.py
│   ├── templates/
│   │   └── auth/
│   │       ├── login.html
│   │       └── register.html
│   └── commands.py
├── config.py
├── requirements.txt
└── run.py

代码实现

1. 配置文件 (config.py)

# config.py
import os
from dotenv import load_dotenv

# 加载环境变量
load_dotenv()

class Config:
    SECRET_KEY = os.environ.get('SECRET_KEY') or 'your-secret-key'
    SQLALCHEMY_DATABASE_URI = os.environ.get('DATABASE_URL') or \
        'postgresql://your_user:your_password@localhost/your_db'
    SQLALCHEMY_TRACK_MODIFICATIONS = False
    DEBUG = True

2. 应用工厂 (app/init.py)

# app/__init__.py
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate
from flask_login import LoginManager
from config import Config

db = SQLAlchemy()
migrate = Migrate()
login_manager = LoginManager()
login_manager.login_view = 'auth.login'

def create_app(config_class=Config):
    app = Flask(__name__)
    app.config.from_object(config_class)
    
    # 初始化扩展
    db.init_app(app)
    migrate.init_app(app, db)
    login_manager.init_app(app)
    
    # 注册蓝图
    from app.auth import bp as auth_bp
    app.register_blueprint(auth_bp, url_prefix='/auth')
    
    # 注册自定义命令
    from app import commands
    app.cli.add_command(commands.init_db_command)
    app.cli.add_command(commands.seed_db_command)
    
    return app

3. 用户模型 (app/models.py)

# app/models.py
from datetime import datetime
from app import db
from flask_login import UserMixin
from werkzeug.security import generate_password_hash, check_password_hash

class User(db.Model, UserMixin):
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(64), index=True, unique=True, nullable=False)
    email = db.Column(db.String(120), index=True, unique=True, nullable=False)
    password_hash = db.Column(db.String(128), nullable=False)
    created_at = db.Column(db.DateTime, default=datetime.utcnow)
    last_login = db.Column(db.DateTime)
    is_active = db.Column(db.Boolean, default=True)
    
    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)
    
    def __repr__(self):
        return f'{self.username}>'

4. 认证蓝图 (app/auth)

表单 (app/auth/forms.py)
# app/auth/forms.py
from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, SubmitField, BooleanField
from wtforms.validators import DataRequired, Email, Length, EqualTo

class LoginForm(FlaskForm):
    username = StringField('用户名', validators=[DataRequired(), Length(min=4, max=64)])
    password = PasswordField('密码', validators=[DataRequired(), Length(min=6)])
    remember = BooleanField('记住我')
    submit = SubmitField('登录')

class RegistrationForm(FlaskForm):
    username = StringField('用户名', validators=[DataRequired(), Length(min=4, max=64)])
    email = StringField('邮箱', validators=[DataRequired(), Email()])
    password = PasswordField('密码', validators=[DataRequired(), Length(min=6)])
    password2 = PasswordField(
        '确认密码', validators=[DataRequired(), EqualTo('password')])
    submit = SubmitField('注册')
路由 (app/auth/routes.py)
# app/auth/routes.py
from flask import Blueprint, render_template, redirect, url_for, flash, request
from flask_login import login_user, logout_user, current_user, login_required
from app import db
from app.auth.forms import LoginForm, RegistrationForm
from app.models import User

bp = Blueprint('auth', __name__)

@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(username=form.username.data).first()
        
        if user and user.check_password(form.password.data):
            login_user(user, remember=form.remember.data)
            next_page = request.args.get('next')
            flash('登录成功!', 'success')
            return redirect(next_page or url_for('main.index'))
        else:
            flash('用户名或密码错误', 'danger')
    
    return render_template('auth/login.html', title='登录', form=form)

@bp.route('/register', methods=['GET', 'POST'])
def register():
    if current_user.is_authenticated:
        return redirect(url_for('main.index'))
    
    form = RegistrationForm()
    if form.validate_on_submit():
        user = User(username=form.username.data, email=form.email.data)
        user.set_password(form.password.data)
        db.session.add(user)
        db.session.commit()
        flash('注册成功!您现在可以登录了', 'success')
        return redirect(url_for('auth.login'))
    
    return render_template('auth/register.html', title='注册', form=form)

@bp.route('/logout')
@login_required
def logout():
    logout_user()
    flash('您已成功退出登录', 'info')
    return redirect(url_for('main.index'))

5. 命令行工具 (app/commands.py)

# app/commands.py
import click
from app import db
from app.models import User

def init_db_command():
    """初始化数据库"""
    db.create_all()
    click.echo('数据库已初始化')

def seed_db_command():
    """添加基础用户数据"""
    users = [
        {'username': 'admin', 'email': '[email protected]', 'password': 'admin123'},
        {'username': 'user1', 'email': '[email protected]', 'password': 'user123'},
        {'username': 'user2', 'email': '[email protected]', 'password': 'user123'}
    ]
    
    for user_data in users:
        user = User.query.filter_by(username=user_data['username']).first()
        if not user:
            user = User(username=user_data['username'], email=user_data['email'])
            user.set_password(user_data['password'])
            db.session.add(user)
    
    db.session.commit()
    click.echo('基础用户数据已添加')

6. 模板文件 (app/templates/auth)

登录页面 (login.html)
DOCTYPE html>
<html>
<head>
    <title>登录title>
    <link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet">
head>
<body>
    <div class="container mt-5">
        <div class="row justify-content-center">
            <div class="col-md-6">
                <h2 class="mb-4">用户登录h2>
                
                {% with messages = get_flashed_messages(with_categories=true) %}
                    {% if messages %}
                        {% for category, message in messages %}
                            <div class="alert alert-{{ category }} alert-dismissible fade show" role="alert">
                                {{ message }}
                                <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close">button>
                            div>
                        {% endfor %}
                    {% endif %}
                {% endwith %}
                
                <form method="POST" action="{{ url_for('auth.login') }}">
                    {{ form.hidden_tag() }}
                    <div class="mb-3">
                        {{ form.username.label(class="form-label") }}
                        {{ form.username(class="form-control", placeholder="请输入用户名") }}
                        {% for error in form.username.errors %}
                            <div class="text-danger">{{ error }}div>
                        {% endfor %}
                    div>
                    <div class="mb-3">
                        {{ form.password.label(class="form-label") }}
                        {{ form.password(class="form-control", placeholder="请输入密码") }}
                        {% for error in form.password.errors %}
                            <div class="text-danger">{{ error }}div>
                        {% endfor %}
                    div>
                    <div class="mb-3 form-check">
                        {{ form.remember(class="form-check-input") }}
                        {{ form.remember.label(class="form-check-label") }}
                    div>
                    <button type="submit" class="btn btn-primary">登录button>
                    <a href="{{ url_for('auth.register') }}" class="btn btn-link">注册新账号a>
                form>
            div>
        div>
    div>
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js">script>
body>
html>
注册页面 (register.html)
DOCTYPE html>
<html>
<head>
    <title>注册title>
    <link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet">
head>
<body>
    <div class="container mt-5">
        <div class="row justify-content-center">
            <div class="col-md-6">
                <h2 class="mb-4">用户注册h2>
                
                {% with messages = get_flashed_messages(with_categories=true) %}
                    {% if messages %}
                        {% for category, message in messages %}
                            <div class="alert alert-{{ category }} alert-dismissible fade show" role="alert">
                                {{ message }}
                                <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close">button>
                            div>
                        {% endfor %}
                    {% endif %}
                {% endwith %}
                
                <form method="POST" action="{{ url_for('auth.register') }}">
                    {{ form.hidden_tag() }}
                    <div class="mb-3">
                        {{ form.username.label(class="form-label") }}
                        {{ form.username(class="form-control", placeholder="请输入用户名") }}
                        {% for error in form.username.errors %}
                            <div class="text-danger">{{ error }}div>
                        {% endfor %}
                    div>
                    <div class="mb-3">
                        {{ form.email.label(class="form-label") }}
                        {{ form.email(class="form-control", placeholder="请输入邮箱") }}
                        {% for error in form.email.errors %}
                            <div class="text-danger">{{ error }}div>
                        {% endfor %}
                    div>
                    <div class="mb-3">
                        {{ form.password.label(class="form-label") }}
                        {{ form.password(class="form-control", placeholder="请输入密码") }}
                        {% for error in form.password.errors %}
                            <div class="text-danger">{{ error }}div>
                        {% endfor %}
                    div>
                    <div class="mb-3">
                        {{ form.password2.label(class="form-label") }}
                        {{ form.password2(class="form-control", placeholder="请再次输入密码") }}
                        {% for error in form.password2.errors %}
                            <div class="text-danger">{{ error }}div>
                        {% endfor %}
                    div>
                    <button type="submit" class="btn btn-primary">注册button>
                    <a href="{{ url_for('auth.login') }}" class="btn btn-link">已有账号?立即登录a>
                form>
            div>
        div>
    div>
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js">script>
body>
html>

7. 主应用文件 (run.py)

# run.py
from app import create_app

app = create_app()

if __name__ == '__main__':
    app.run(debug=True)

安装和运行步骤

1. 创建虚拟环境并安装依赖

python -m venv venv
source venv/bin/activate  # Linux/macOS
venv\Scripts\activate     # Windows

pip install flask flask-sqlalchemy flask-migrate flask-login flask-wtf psycopg2-binary python-dotenv

2. 创建PostgreSQL数据库

# 连接到PostgreSQL
sudo -u postgres psql

# 创建数据库和用户
CREATE DATABASE flask_login;
CREATE USER flask_user WITH PASSWORD 'your_password';
GRANT ALL PRIVILEGES ON DATABASE flask_login TO flask_user;
\q

3. 配置环境变量

创建 .env 文件:

SECRET_KEY=your_very_secret_key
DATABASE_URL=postgresql://flask_user:your_password@localhost/flask_login

4. 初始化数据库

# 初始化数据库迁移
flask db init

# 创建迁移脚本
flask db migrate -m "Initial migration"

# 应用迁移
flask db upgrade

# 初始化数据库表结构
flask init-db

# 添加基础用户数据
flask seed-db

5. 运行应用

python run.py

功能说明

  1. 用户认证

    • 登录/注册/退出功能
    • 密码哈希存储
    • 记住我功能
  2. 数据库管理

    • PostgreSQL数据库存储
    • Flask-Migrate数据库迁移
    • 命令行工具初始化
  3. 模块化架构

    • 蓝图组织路由
    • 应用工厂模式
    • 分离模型、视图、模板
  4. 安全特性

    • CSRF保护
    • 密码强度验证
    • 环境变量配置敏感信息

使用说明

  1. 访问 http://localhost:5000/auth/login 登录
  2. 使用命令行工具初始化数据库:
    • flask init-db 初始化数据库结构
    • flask seed-db 添加基础用户数据
  3. 测试用户:
    • admin/admin123
    • user1/user123
    • user2/user123

你可能感兴趣的:(#,flask,flask)