PHP 多用户类型登录

wen PHP项目 5

本文目录导读:

PHP 多用户类型登录

  1. 方案一:基于用户表字段区分(最简单)
  2. 方案二:使用独立表(更专业)
  3. 方案三:使用 RBAC(角色权限控制) - 最灵活
  4. 方案四:JWT Token 认证(API 场景)
  5. 最佳实践建议
  6. 前端用户类型选择界面

在 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]);
}

最佳实践建议

  1. 密码安全:永远使用 password_hash()password_verify()
  2. 防SQL注入:使用 PDO 预处理语句
  3. CSRF防护:使用 CSRF Token
  4. 会话安全:session 设置 httponlysamesite
  5. 角色权限分离:根据需求选择最合适的方案
  6. 登录日志:记录登录时间、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)是现代首选

建议从方案一开始,随着需求增长再逐步升级。

抱歉,评论功能暂时关闭!