本文目录导读:

在 PHP 中实现多用户类型登录,通常有以下几种设计方案,我将从最简单的方案到最灵活的架构逐一介绍,并给出完整的代码示例。
基于用户表字段区分(最简单)
这是最常见的做法,在 users 表中添加 role 字段(如:admin, teacher, student)。
数据库设计
CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(50) UNIQUE NOT NULL,
password VARCHAR(255) NOT NULL,
role ENUM('admin', 'teacher', 'student') NOT NULL DEFAULT 'student',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
登录处理代码
<?php
// login.php
session_start();
require_once 'db.php'; // 数据库连接
$error = '';
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$username = trim($_POST['username']);
$password = $_POST['password'];
if (empty($username) || empty($password)) {
$error = '用户名和密码不能为空';
} else {
// 预处理语句防止SQL注入
$stmt = $pdo->prepare("SELECT * FROM users WHERE username = ?");
$stmt->execute([$username]);
$user = $stmt->fetch(PDO::FETCH_ASSOC);
if ($user && password_verify($password, $user['password'])) {
// 登录成功,存储用户信息到session
$_SESSION['user_id'] = $user['id'];
$_SESSION['username'] = $user['username'];
$_SESSION['role'] = $user['role'];
// 根据角色跳转到不同页面
switch ($user['role']) {
case 'admin':
header('Location: admin/dashboard.php');
break;
case 'teacher':
header('Location: teacher/dashboard.php');
break;
case 'student':
header('Location: student/dashboard.php');
break;
default:
header('Location: login.php?error=invalid_role');
}
exit;
} else {
$error = '用户名或密码错误';
}
}
}
?>
<!DOCTYPE html>
<html>
<head>多用户登录</title>
</head>
<body>
<form method="POST" action="">
<?php if ($error): ?>
<div style="color: red;"><?php echo $error; ?></div>
<?php endif; ?>
<input type="text" name="username" placeholder="用户名" required>
<input type="password" name="password" placeholder="密码" required>
<button type="submit">登录</button>
</form>
</body>
</html>
角色权限检查(中间件)
<?php
// check_auth.php - 角色权限检查
session_start();
function checkRole($allowed_roles = ['admin', 'teacher', 'student']) {
if (!isset($_SESSION['user_id'])) {
header('Location: login.php');
exit;
}
if (!in_array($_SESSION['role'], $allowed_roles)) {
header('Location: unauthorized.php');
exit;
}
}
// 使用示例:在 admin/dashboard.php 中
require_once 'check_auth.php';
checkRole(['admin']); // 只允许管理员
使用独立表(更专业)
每个用户类型有自己的数据表,通过 user_id 关联。
数据库设计
-- 用户主表
CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(50) UNIQUE NOT NULL,
password VARCHAR(255) NOT NULL,
user_type ENUM('admin', 'teacher', 'student') NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- 教师详细信息表
CREATE TABLE teachers (
id INT PRIMARY KEY,
user_id INT NOT NULL,
teacher_code VARCHAR(20) UNIQUE,
department VARCHAR(100),
hire_date DATE,
FOREIGN KEY (user_id) REFERENCES users(id)
);
-- 学生详细信息表
CREATE TABLE students (
id INT PRIMARY KEY,
user_id INT NOT NULL,
student_code VARCHAR(20) UNIQUE,
class_name VARCHAR(50),
enrollment_year YEAR,
FOREIGN KEY (user_id) REFERENCES users(id)
);
-- 管理员详细信息表
CREATE TABLE admins (
id INT PRIMARY KEY,
user_id INT NOT NULL,
admin_level INT DEFAULT 1,
last_login_ip VARCHAR(45),
FOREIGN KEY (user_id) REFERENCES users(id)
);
登录处理代码
<?php
// login.php - 更复杂的登录逻辑
session_start();
require_once 'db.php';
function getUserProfile($pdo, $user_id, $user_type) {
switch ($user_type) {
case 'teacher':
$stmt = $pdo->prepare("SELECT * FROM teachers WHERE user_id = ?");
break;
case 'student':
$stmt = $pdo->prepare("SELECT * FROM students WHERE user_id = ?");
break;
case 'admin':
$stmt = $pdo->prepare("SELECT * FROM admins WHERE user_id = ?");
break;
default:
return null;
}
$stmt->execute([$user_id]);
return $stmt->fetch(PDO::FETCH_ASSOC);
}
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$username = trim($_POST['username']);
$password = $_POST['password'];
$user_type = $_POST['user_type']; // 前端选择用户类型
// 查找用户
$stmt = $pdo->prepare("SELECT * FROM users WHERE username = ? AND user_type = ?");
$stmt->execute([$username, $user_type]);
$user = $stmt->fetch(PDO::FETCH_ASSOC);
if ($user && password_verify($password, $user['password'])) {
// 获取用户详细信息
$profile = getUserProfile($pdo, $user['id'], $user_type);
// 存储完整会话
$_SESSION['auth'] = [
'user_id' => $user['id'],
'username' => $user['username'],
'user_type' => $user_type,
'profile' => $profile,
'login_time' => time()
];
// 记录登录日志
$stmt = $pdo->prepare("INSERT INTO login_logs (user_id, login_time, ip_address) VALUES (?, NOW(), ?)");
$stmt->execute([$user['id'], $_SERVER['REMOTE_ADDR']]);
// 跳转
$redirect_map = [
'admin' => '/admin/dashboard.php',
'teacher' => '/teacher/dashboard.php',
'student' => '/student/dashboard.php'
];
header('Location: ' . $redirect_map[$user_type]);
exit;
} else {
$error = '用户名/密码/用户类型不匹配';
}
}
?>
使用 RBAC(角色权限控制) - 最灵活
这是企业级应用最常用的方案,适合权限复杂的系统。
数据库设计
-- 用户表
CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(50) UNIQUE NOT NULL,
password VARCHAR(255) NOT NULL,
email VARCHAR(100),
is_active BOOLEAN DEFAULT TRUE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- 角色表
CREATE TABLE roles (
id INT AUTO_INCREMENT PRIMARY KEY,
role_name VARCHAR(50) UNIQUE NOT NULL,
role_description TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- 用户-角色关联表(一个用户可以有多个角色)
CREATE TABLE user_roles (
user_id INT NOT NULL,
role_id INT NOT NULL,
assigned_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (user_id, role_id),
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
FOREIGN KEY (role_id) REFERENCES roles(id) ON DELETE CASCADE
);
-- 权限表
CREATE TABLE permissions (
id INT AUTO_INCREMENT PRIMARY KEY,
permission_name VARCHAR(50) UNIQUE NOT NULL,
module VARCHAR(50) NOT NULL,
action VARCHAR(50) NOT NULL
);
-- 角色-权限关联表
CREATE TABLE role_permissions (
role_id INT NOT NULL,
permission_id INT NOT NULL,
PRIMARY KEY (role_id, permission_id),
FOREIGN KEY (role_id) REFERENCES roles(id) ON DELETE CASCADE,
FOREIGN KEY (permission_id) REFERENCES permissions(id) ON DELETE CASCADE
);
RBAC 核心类
<?php
// RBAC.php - 权限控制核心类
class Rbac {
private $pdo;
public function __construct($pdo) {
$this->pdo = $pdo;
}
// 获取用户的所有角色
public function getUserRoles($user_id) {
$stmt = $this->pdo->prepare("
SELECT r.role_name FROM roles r
INNER JOIN user_roles ur ON r.id = ur.role_id
WHERE ur.user_id = ?
");
$stmt->execute([$user_id]);
return $stmt->fetchAll(PDO::FETCH_COLUMN);
}
// 检查用户是否有特定权限
public function hasPermission($user_id, $module, $action) {
$stmt = $this->pdo->prepare("
SELECT COUNT(*) FROM user_roles ur
INNER JOIN role_permissions rp ON ur.role_id = rp.role_id
INNER JOIN permissions p ON rp.permission_id = p.id
WHERE ur.user_id = ?
AND p.module = ?
AND p.action = ?
");
$stmt->execute([$user_id, $module, $action]);
return $stmt->fetchColumn() > 0;
}
// 检查用户是否有特定角色
public function hasRole($user_id, $role_name) {
$roles = $this->getUserRoles($user_id);
return in_array($role_name, $roles);
}
// 获取用户的最大权限级别
public function getMaxRoleLevel($user_id) {
$stmt = $this->pdo->prepare("
SELECT MIN(id) as level FROM roles
WHERE id IN (
SELECT role_id FROM user_roles WHERE user_id = ?
)
");
$stmt->execute([$user_id]);
return $stmt->fetchColumn() ?? 999;
}
}
权限检查使用方法
<?php
// 在任何需要权限的页面中使用
session_start();
require_once 'db.php';
require_once 'RBAC.php';
$rbac = new Rbac($pdo);
// 方式1:检查角色
if (!$rbac->hasRole($_SESSION['user_id'], 'admin')) {
header('Location: /403.php');
exit;
}
// 方式2:检查具体操作权限
if (!$rbac->hasPermission($_SESSION['user_id'], 'user', 'delete')) {
header('Location: /403.php');
exit;
}
// 方式3:在函数中使用
function requirePermission($pdo, $module, $action) {
$rbac = new Rbac($pdo);
if (!isset($_SESSION['user_id']) || !$rbac->hasPermission($_SESSION['user_id'], $module, $action)) {
throw new Exception("权限不足");
}
}
JWT Token 认证(API 场景)
如果你是做前后端分离的 API 服务,可以使用 JWT 认证。
<?php
// jwt_auth.php - JWT 认证类
require_once 'vendor/autoload.php'; // 使用firebase/php-jwt库
use Firebase\JWT\JWT;
use Firebase\JWT\Key;
class JwtAuth {
private $secret_key = 'your-secret-key';
private $algorithm = 'HS256';
// 生成Token
public function generateToken($user_id, $user_type, $expire_hours = 24) {
$payload = [
'user_id' => $user_id,
'user_type' => $user_type,
'iat' => time(),
'exp' => time() + ($expire_hours * 3600)
];
return JWT::encode($payload, $this->secret_key, $this->algorithm);
}
// 验证Token
public function validateToken($token) {
try {
$decoded = JWT::decode($token, new Key($this->secret_key, $this->algorithm));
return $decoded; // 返回解析后的payload
} catch (Exception $e) {
return null; // Token无效
}
}
// 获取当前用户信息
public function getCurrentUser($token) {
$decoded = $this->validateToken($token);
if (!$decoded) {
return null;
}
return [
'user_id' => $decoded->user_id,
'user_type' => $decoded->user_type
];
}
}
// 登录API示例
if ($_SERVER['REQUEST_METHOD'] === 'POST' && $path === '/api/login') {
$data = json_decode(file_get_contents('php://input'), true);
$username = $data['username'];
$password = $data['password'];
$user_type = $data['user_type'];
// ... 验证用户密码 ...
// 生成token
$jwtAuth = new JwtAuth();
$token = $jwtAuth->generateToken($user['id'], $user_type);
header('Content-Type: application/json');
echo json_encode(['token' => $token]);
}
最佳实践建议
- 密码安全:永远使用
password_hash()和password_verify() - 防SQL注入:使用 PDO 预处理语句
- CSRF防护:使用 CSRF Token
- 会话安全:session 设置
httponly和samesite - 角色权限分离:根据需求选择最合适的方案
- 登录日志:记录登录时间、IP、设备等
前端用户类型选择界面
<!-- login_form.html -->
<form action="login.php" method="POST">
<select name="user_type" required>
<option value="">选择用户类型</option>
<option value="admin">管理员</option>
<option value="teacher">教师</option>
<option value="student">学生</option>
</select>
<input type="text" name="username" placeholder="用户名" required>
<input type="password" name="password" placeholder="密码" required>
<!-- CSRF Token -->
<input type="hidden" name="csrf_token" value="<?php echo $_SESSION['csrf_token']; ?>">
<button type="submit">登录</button>
</form>
选择哪种方案主要取决于你的项目需求:
- 小项目:方案一(角色字段)就够了
- 中大型项目:方案二(独立表)更结构化
- 复杂权限系统:方案三(RBAC)最灵活
- API服务:方案四(JWT)是现代首选
建议从方案一开始,随着需求增长再逐步升级。