首页 / PHP 入门教程 / 综合案例:用户管理系统

PHP 入门教程

综合案例:用户管理系统

本教程共 65 篇 · 第 63 篇 · 更新于 2026-07-24 · 约 14 分钟阅读

PHPPHP8综合案例用户管理Session密码哈希CRUD

63. 综合案例:用户管理系统

本节目标:综合运用所学知识,从零搭建一个包含注册、登录、CRUD 和权限控制的完整用户管理系统。

本节是一个完整的实战项目,将用到 PDO、预处理语句、Session、密码哈希、简单路由等知识点。项目虽小,但五脏俱全。


1. 项目结构

user-management/
├── config/
│   └── database.php      # 数据库配置
├── public/
│   ├── index.php         # 入口文件
│   └── style.css         # 样式文件
├── src/
│   ├── User.php          # 用户模型
│   └── Auth.php          # 认证类
├── templates/
│   ├── layout.php        # 公共布局
│   ├── login.php         # 登录页
│   ├── register.php      # 注册页
│   └── list.php          # 用户列表
└── init.sql              # 数据库初始化
Note

public/ 作为 Web 根目录,其他文件放在外部防止直接访问,这是安全的基本做法。


2. 数据库初始化

-- init.sql
CREATE DATABASE IF NOT EXISTS user_demo CHARACTER SET utf8mb4;
USE user_demo;

CREATE TABLE users (
    id         INT AUTO_INCREMENT PRIMARY KEY,
    username   VARCHAR(50) NOT NULL UNIQUE,
    email      VARCHAR(100) NOT NULL UNIQUE,
    password   VARCHAR(255) NOT NULL,
    role       ENUM('admin','user') DEFAULT 'user',
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB;

3. 数据库配置

<?php
// config/database.php
function getPdo(): PDO
{
    static $pdo = null;
    if ($pdo === null) {
        $dsn = 'mysql:host=localhost;dbname=user_demo;charset=utf8mb4';
        $pdo = new PDO($dsn, 'root', '123456', [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        ]);
    }
    return $pdo;
}

4. 用户模型

<?php
// src/User.php
class User
{
    private PDO $pdo;

    public function __construct()
    {
        $this->pdo = getPdo();
    }

    public function create(string $username, string $email, string $password): int
    {
        $hash = password_hash($password, PASSWORD_DEFAULT);
        $stmt = $this->pdo->prepare(
            "INSERT INTO users (username, email, password) VALUES (:u, :e, :p)"
        );
        $stmt->execute([':u' => $username, ':e' => $email, ':p' => $hash]);
        return (int) $this->pdo->lastInsertId();
    }

    public function findByEmail(string $email): ?array
    {
        $stmt = $this->pdo->prepare("SELECT * FROM users WHERE email = :e");
        $stmt->execute([':e' => $email]);
        return $stmt->fetch() ?: null;
    }

    public function findAll(): array
    {
        return $this->pdo->query("SELECT id, username, email, role, created_at FROM users ORDER BY id DESC")
            ->fetchAll();
    }

    public function delete(int $id): bool
    {
        $stmt = $this->pdo->prepare("DELETE FROM users WHERE id = :id");
        return $stmt->execute([':id' => $id]);
    }
}
Tip

password_hash() 使用 bcrypt 算法,PHP 推荐的密码哈希方式。不要用 md5sha1


5. 认证类

<?php
// src/Auth.php
session_start();

class Auth
{
    public static function login(array $user): void
    {
        $_SESSION['user_id'] = $user['id'];
        $_SESSION['username'] = $user['username'];
        $_SESSION['role'] = $user['role'];
    }

    public static function logout(): void
    {
        $_SESSION = [];
        session_destroy();
    }

    public static function check(): bool
    {
        return isset($_SESSION['user_id']);
    }

    public static function isAdmin(): bool
    {
        return ($_SESSION['role'] ?? '') === 'admin';
    }

    public static function user(): ?array
    {
        if (!self::check()) return null;
        return [
            'id'       => $_SESSION['user_id'],
            'username' => $_SESSION['username'],
            'role'     => $_SESSION['role'],
        ];
    }
}

6. 入口与路由

<?php
// public/index.php
require_once __DIR__ . '/../config/database.php';
require_once __DIR__ . '/../src/User.php';
require_once __DIR__ . '/../src/Auth.php';

$action = $_GET['action'] ?? 'login';

switch ($action) {
    case 'register':
        handleRegister();
        break;
    case 'login':
        handleLogin();
        break;
    case 'logout':
        Auth::logout();
        header('Location: ?action=login');
        exit;
    case 'list':
        requireAuth();
        $users = (new User())->findAll();
        include __DIR__ . '/../templates/list.php';
        break;
    case 'delete':
        requireAuth();
        if (!Auth::isAdmin()) die('无权操作');
        $id = (int) ($_GET['id'] ?? 0);
        if ($id > 0) (new User())->delete($id);
        header('Location: ?action=list');
        exit;
    default:
        header('Location: ?action=login');
        exit;
}

function handleRegister(): void
{
    $error = '';
    if ($_SERVER['REQUEST_METHOD'] === 'POST') {
        $username = trim($_POST['username'] ?? '');
        $email    = trim($_POST['email'] ?? '');
        $password = $_POST['password'] ?? '';

        // 基础验证
        if (strlen($username) < 2 || strlen($username) > 50) {
            $error = '用户名长度需在 2-50 个字符之间';
        } elseif (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
            $error = '邮箱格式不正确';
        } elseif (strlen($password) < 6) {
            $error = '密码至少 6 位';
        } else {
            $user = new User();
            try {
                $user->create($username, $email, $password);
                header('Location: ?action=login');
                exit;
            } catch (PDOException $e) {
                $error = '用户名或邮箱已存在';
            }
        }
    }
    include __DIR__ . '/../templates/register.php';
}

function handleLogin(): void
{
    $error = '';
    if ($_SERVER['REQUEST_METHOD'] === 'POST') {
        $user = new User();
        $record = $user->findByEmail($_POST['email']);
        if ($record && password_verify($_POST['password'], $record['password'])) {
            Auth::login($record);
            header('Location: ?action=list');
            exit;
        }
        $error = '邮箱或密码错误';
    }
    include __DIR__ . '/../templates/login.php';
}

function requireAuth(): void
{
    if (!Auth::check()) {
        header('Location: ?action=login');
        exit;
    }
}
Note

真实项目应使用成熟的 Router 组件(如 FastRoute、Symfony Routing),这里用 switch 是为了清晰展示原理。


7. 模板示例

公共布局

<?php
// templates/layout.php
?>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title><?= htmlspecialchars($title) ?></title>
    <link rel="stylesheet" href="style.css">
</head>
<body>
    <nav>
        <a href="?action=list">用户列表</a>
        <?php if (Auth::check()): ?>
            <span>当前用户:<?= htmlspecialchars(Auth::user()['username']) ?></span>
            <a href="?action=logout">退出</a>
        <?php endif; ?>
    </nav>
    <main>
        <?= $content ?>
    </main>
</body>
</html>

登录页

<?php
// templates/login.php
$title = '登录';
ob_start();
?>
<h2>用户登录</h2>
<?php if ($error): ?>
    <p style="color:red"><?= htmlspecialchars($error) ?></p>
<?php endif; ?>
<form method="post">
    <p>邮箱:<input type="email" name="email" required></p>
    <p>密码:<input type="password" name="password" required></p>
    <p><button type="submit">登录</button></p>
    <p><a href="?action=register">还没有账号?注册</a></p>
</form>
<?php
$content = ob_get_clean();
include __DIR__ . '/layout.php';
Tip

htmlspecialchars() 防止 XSS 攻击。所有输出到页面的变量都应过滤。

注册页

<?php
// templates/register.php
$title = '注册';
ob_start();
?>
<h2>用户注册</h2>
<?php if (!empty($error)): ?>
    <p style="color:red"><?= htmlspecialchars($error) ?></p>
<?php endif; ?>
<form method="post">
    <p>用户名:<input type="text" name="username" required></p>
    <p>邮箱:<input type="email" name="email" required></p>
    <p>密码:<input type="password" name="password" required minlength="6"></p>
    <p><button type="submit">注册</button></p>
    <p><a href="?action=login">已有账号?登录</a></p>
</form>
<?php
$content = ob_get_clean();
include __DIR__ . '/layout.php';

用户列表页

<?php
// templates/list.php
$title = '用户列表';
ob_start();
?>
<h2>用户列表</h2>
<?php if (Auth::isAdmin()): ?>
    <p>管理员模式</p>
<?php endif; ?>
<table border="1">
    <tr>
        <th>ID</th>
        <th>用户名</th>
        <th>邮箱</th>
        <th>角色</th>
        <th>注册时间</th>
        <?php if (Auth::isAdmin()): ?>
            <th>操作</th>
        <?php endif; ?>
    </tr>
    <?php foreach ($users as $user): ?>
    <tr>
        <td><?= htmlspecialchars($user['id']) ?></td>
        <td><?= htmlspecialchars($user['username']) ?></td>
        <td><?= htmlspecialchars($user['email']) ?></td>
        <td><?= htmlspecialchars($user['role']) ?></td>
        <td><?= htmlspecialchars($user['created_at']) ?></td>
        <?php if (Auth::isAdmin()): ?>
            <td><a href="?action=delete&id=<?= $user['id'] ?>" onclick="return confirm('确认删除?')">删除</a></td>
        <?php endif; ?>
    </tr>
    <?php endforeach; ?>
</table>
<?php
$content = ob_get_clean();
include __DIR__ . '/layout.php';

小结

本节综合运用数据库操作、Session 认证、密码哈希、简单路由等知识,搭建了一个功能完整的用户管理系统。建议在此基础上继续扩展:如添加编辑功能、分页、表单验证等。


来源:参考了 php-the-right-way「Web Application Security」「Password Hashing」、实战经验,改写后所得。