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 推荐的密码哈希方式。不要用md5或sha1。
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」、实战经验,改写后所得。