我的项目 2:Flask 身份验证系统(Python + Flask)

发布: (2025年12月14日 GMT+8 01:30)
5 min read
原文: Dev.to

Source: Dev.to

My project 2 的封面图片:Flask 认证系统(使用 Python + Flask)

🔐 Flask 认证系统

我一直对我们每天使用的网站是如何构建的感到好奇。
我们不断地使用注册、登录和退出等功能,但大多数人从未真正了解背后发生了什么。
我也不例外——这种好奇心与日俱增,我想亲自体验其结构。
于是我决定从零开始构建它,希望通过动手实践,最终弄清这些系统到底是如何运作的。

📂 1. 项目结构

auth_blog/
│── main.py
│── users.db (auto-created)
└── templates/
    ├── home.html
    ├── signup.html
    └── login.html

🧠 2. 项目功能

  • 用户注册(存储哈希密码)
  • 带会话管理的用户登录
  • 使用自定义 login_required 装饰器的受保护仪表盘页面
  • 退出登录功能
  • 基础的迷你博客 UI 布局

这个结构几乎是所有现代 Web 应用的基础。

🖥️ 3. 后端代码 (main.py)

from flask import Flask, render_template, request, redirect, session
import sqlite3
from werkzeug.security import generate_password_hash, check_password_hash

app = Flask(__name__)
app.secret_key = "super_secret_key"

# Login‑required decorator
def login_required(func):
    def wrapper(*args, **kwargs):
        if "user_id" not in session:
            return redirect("/login")
        return func(*args, **kwargs)
    wrapper.__name__ = func.__name__
    return wrapper

# Initialize database
def init_db():
    conn = sqlite3.connect("users.db")
    cur = conn.cursor()
    cur.execute("""
    CREATE TABLE IF NOT EXISTS users (
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        username TEXT UNIQUE NOT NULL,
        password TEXT NOT NULL
    )
    """)
    conn.commit()
    conn.close()

init_db()

# Signup
@app.route("/signup", methods=["GET", "POST"])
def signup():
    if request.method == "POST":
        username = request.form["username"]
        password = request.form["password"]
        hashed_pw = generate_password_hash(password)

        conn = sqlite3.connect("users.db")
        cur = conn.cursor()
        try:
            cur.execute(
                "INSERT INTO users (username, password) VALUES (?, ?)",
                (username, hashed_pw)
            )
            conn.commit()
        except sqlite3.IntegrityError:
            return "Signup failed: Username already exists."
        finally:
            conn.close()

        return redirect("/login")

    return render_template("signup.html")

# Login
@app.route("/login", methods=["GET", "POST"])
def login():
    if request.method == "POST":
        username = request.form["username"]
        password = request.form["password"]

        conn = sqlite3.connect("users.db")
        cur = conn.cursor()
        cur.execute(
            "SELECT id, username, password FROM users WHERE username = ?",
            (username,)
        )
        user = cur.fetchone()
        conn.close()

        if user and check_password_hash(user[2], password):
            session["user_id"] = user[0]
            session["username"] = user[1]
            return redirect("/dashboard")
        else:
            return "Login failed: Invalid username or password."

    return render_template("login.html")

# Dashboard (protected)
@app.route("/dashboard")
@login_required
def dashboard():
    return f"Welcome, {session['username']}! (Dashboard Page)"

# Logout
@app.route("/logout")
def logout():
    session.clear()
    return redirect("/login")

# Home
@app.route("/")
def home():
    return render_template("home.html")

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

🖼️ 4. 模板

🏠 home.html

<!DOCTYPE html>
<html>
<head>
    <title>Mini Blog</title>
    <style>
        body { font-family: sans-serif; padding: 20px; background-color: #f4f7f9; }
        .post { 
            border: 1px solid #ddd; 
            padding: 15px; 
            margin-bottom: 15px; 
            border-radius: 8px; 
            background-color: white;
            box-shadow: 0 2px 4px rgba(0,0,0,0.05);
        }
        .top { 
            display: flex; justify-content: space-between; 
            align-items: center; border-bottom: 2px solid #007bff; 
            margin-bottom: 20px; padding-bottom: 10px;
        }
        a { text-decoration: none; color: #007bff; font-weight: bold; }
    </style>
</head>
<body>
    <div class="top">
        <h1>Mini Blog Posts</h1>
        <a href="/new">Write New Post →</a>
    </div>

    {% for post in posts %}
        <div class="post">
            <h3>{{ post[1] }}</h3>
            <p>{{ post[2] }}</p>
        </div>
    {% endfor %}

    {% if not posts %}
        <p>No posts yet. Start writing your first entry!</p>
    {% endif %}
</body>
</html>

🔑 login.html

<!DOCTYPE html>
<html>
<head>
    <title>Login</title>
    <style>
        body { font-family: sans-serif; padding: 20px; }
        form { display: flex; flex-direction: column; width: 300px; gap: 10px; }
        input { padding: 10px; border: 1px solid #ccc; border-radius: 4px; }
        button { padding: 10px; background: #28a745; color: white; 
                 border: none; border-radius: 4px; cursor: pointer; }
        button:hover { background: #1e7e34; }
    </style>
</head>
<body>
    <h2>Log In</h2>
    <form method="POST" action="/login">
        <input type="text" name="username" placeholder="Username" required>
        <input type="password" name="password" placeholder="Password" required>
        <button type="submit">Log In</button>
    </form>
</body>
</html>

📝 signup.html

<!DOCTYPE html>
<html>
<head>
    <title>Sign Up</title>
    <style>
        body { font-family: sans-serif; padding: 20px; }
        form { display: flex; flex-direction: column; width: 300px; gap: 10px; }
        input { padding: 10px; border: 1px solid #ccc; border-radius: 4px; }
        button { padding: 10px; background: #007bff; color: white; 
                 border: none; border-radius: 4px; cursor: pointer; }
        button:hover { background: #0056b3; }
    </style>
</head>
<body>
    <h2>Sign Up</h2>
    <form method="POST" action="/signup">
        <input type="text" name="username" placeholder="Username" required>
        <input type="password" name="password" placeholder="Password" required>
        <button type="submit">Register</button>
    </form>
</body>
</html>

🔧 5. 亲自尝试 — 简单改进

  • 在已认证的仪表盘中添加帖子创建功能
  • 允许用户编辑/删除自己的帖子
  • 为帖子添加时间戳
  • 添加密码重置功能
  • 使用 CSS 样式表改进 UI

这些小改进将帮助你更深入地理解 Web 认证和后端结构。尝试做一两项修改,看看这个项目如何快速演变成真正的应用!

Back to Blog

相关文章

阅读更多 »