You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用PDO与Session的登录系统:登录后会话数据无法存入数据库求助

我帮你梳理下当前代码里的问题,然后一步步修正,让你的基于PDO和Session的登录会话存储功能正常跑起来:

一、先理清现有代码的核心问题
  • config.php里直接执行了用户表查询,应该只保留数据库连接逻辑,作为全局配置文件使用
  • functions.php的checkLoginState函数存在变量未定义、逻辑冗余的问题,session_start()的启动时机也不对
  • createRecord函数的SQL语句有语法错误,DELETE操作的参数绑定完全失效,还多了多余的日期字符串
  • createSession函数没有把关键的token和serial存入Session,会话验证逻辑不完整
  • createString的随机字符串生成逻辑会导致长度不足,还可能出现索引越界的问题
二、修正后的完整代码

1. 修正后的config.php(仅保留数据库连接)

<?php
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);

// 开启PDO异常模式,方便调试数据库错误
try {
    $dbh = new PDO('mysql:host=localhost;dbname=rineva', 'root', '');
    $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch(PDOException $e) {
    die("数据库连接失败: " . $e->getMessage());
}
?>

2. 修正后的functions.php

<?php
// 统一在文件开头启动会话,避免位置混乱导致的会话失效问题
session_start();

class func {
    /**
     * 验证登录状态:匹配Cookie、Session与数据库会话记录
     */
    public static function checkLoginState($dbh) {
        // 先检查必要的Cookie是否存在
        if (!isset($_COOKIE['user_id']) || !isset($_COOKIE['token']) || !isset($_COOKIE['serial'])) {
            return false;
        }

        $user_id = $_COOKIE['user_id'];
        $token = $_COOKIE['token'];
        $serial = $_COOKIE['serial'];

        try {
            // 查询数据库中的有效会话记录
            $query = "SELECT * FROM sessions WHERE sessions_userid = :user_id AND sessions_token = :token AND sessions_serial = :serial";
            $stmt = $dbh->prepare($query);
            $stmt->execute([
                ':user_id' => $user_id,
                ':token' => $token,
                ':serial' => $serial
            ]);
            $sessionRecord = $stmt->fetch(PDO::FETCH_ASSOC);

            // 验证记录存在,且与Session数据完全匹配
            if ($sessionRecord && 
                $sessionRecord['sessions_userid'] == $_SESSION['user_id'] &&
                $sessionRecord['sessions_token'] == $_SESSION['token'] &&
                $sessionRecord['sessions_serial'] == $_SESSION['serial']) {
                return true;
            }
        } catch(PDOException $e) {
            error_log("登录状态检查失败: " . $e->getMessage());
        }

        return false;
    }

    /**
     * 创建新会话记录:先清理旧记录,再插入新记录,同步设置Cookie和Session
     */
    public static function createRecord($dbh, $user_username, $user_id) {
        try {
            // 先删除该用户的旧会话(如果允许多端登录可注释此段)
            $deleteStmt = $dbh->prepare("DELETE FROM sessions WHERE sessions_userid = :user_id");
            $deleteStmt->execute([':user_id' => $user_id]);

            // 生成20位随机的token和serial
            $token = self::createString(20);
            $serial = self::createString(20);

            // 插入新会话记录,自动写入创建时间
            $insertStmt = $dbh->prepare("
                INSERT INTO sessions(sessions_userid, sessions_token, sessions_serial, sessions_created) 
                VALUES (:user_id, :token, :serial, NOW())
            ");
            $insertStmt->execute([
                ':user_id' => $user_id,
                ':token' => $token,
                ':serial' => $serial
            ]);

            // 设置登录Cookie和Session
            self::createCookie($user_username, $user_id, $token, $serial);
            self::createSession($user_username, $user_id, $token, $serial);

            return true;
        } catch(PDOException $e) {
            error_log("会话记录创建失败: " . $e->getMessage());
            return false;
        }
    }

    /**
     * 设置登录Cookie,添加httponly属性提升安全性
     */
    public static function createCookie($user_username, $user_id, $token, $serial) {
        $expireTime = time() + (86400 * 30); // 30天有效期
        // httponly属性防止XSS攻击窃取Cookie,生产环境建议开启secure属性(仅HTTPS传输)
        setcookie('user_id', $user_id, $expireTime, "/", "", false, true);
        setcookie('token', $token, $expireTime, "/", "", false, true);
        setcookie('serial', $serial, $expireTime, "/", "", false, true);
        setcookie('user_username', $user_username, $expireTime, "/", "", false, true);
    }

    /**
     * 初始化登录Session,存储关键验证字段
     */
    public static function createSession($user_username, $user_id, $token, $serial) {
        $_SESSION['user_username'] = $user_username;
        $_SESSION['user_id'] = $user_id;
        $_SESSION['token'] = $token;
        $_SESSION['serial'] = $serial;
    }

    /**
     * 生成指定长度的随机字符串,优化逻辑避免索引越界
     */
    public static function createString($len) {
        $charPool = "1lksjrienj4hjtJHBNKq5SWndmrtjihush45bhe0qwsdcnNER56NWMLOIwsQ";
        $poolLen = strlen($charPool);
        $result = '';

        for ($i = 0; $i < $len; $i++) {
            $result .= $charPool[rand(0, $poolLen - 1)];
        }

        return $result;
    }
}
?>

3. 补充登录示例页面(login.php)

<?php
require_once 'config.php';
require_once 'functions.php';

// 处理POST登录请求
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
    $username = trim($_POST['username']);
    $password = $_POST['password'];

    try {
        // 查询用户(注意:实际项目必须用密码哈希验证,禁止明文存储!)
        $stmt = $dbh->prepare("SELECT user_id, user_username FROM user WHERE user_username = :username AND user_password = :password");
        $stmt->execute([
            ':username' => $username,
            ':password' => $password // 实际项目替换为password_verify($password, $dbPassword)
        ]);
        $user = $stmt->fetch(PDO::FETCH_ASSOC);

        if ($user) {
            // 创建会话记录并跳转
            if (func::createRecord($dbh, $user['user_username'], $user['user_id'])) {
                header("Location: dashboard.php");
                exit;
            } else {
                echo "会话创建失败,请重试";
            }
        } else {
            echo "用户名或密码错误";
        }
    } catch(PDOException $e) {
        echo "登录失败: " . $e->getMessage();
    }
}
?>

<!-- 登录表单 -->
<form method="POST" action="login.php">
    <input type="text" name="username" placeholder="用户名" required>
    <input type="password" name="password" placeholder="密码" required>
    <button type="submit">登录</button>
</form>

4. 补充登录验证页面(dashboard.php)

<?php
require_once 'config.php';
require_once 'functions.php';

// 检查登录状态,未登录则跳转至登录页
if (!func::checkLoginState($dbh)) {
    header("Location: login.php");
    exit;
}

echo "欢迎回来," . $_SESSION['user_username'];
?>
三、关键改进说明
  • 会话启动统一:在functions.php开头统一调用session_start(),避免因启动时机混乱导致的会话失效
  • PDO异常模式:开启异常模式后,数据库错误会被主动抛出,方便调试和排查问题
  • SQL语法修复:修正了INSERT/DELETE语句的语法错误,添加了会话创建时间的自动写入
  • 安全性提升:Cookie添加httponly属性防止XSS攻击,生产环境建议开启secure属性限制HTTPS传输
  • 密码安全提醒:实际项目中必须使用password_hash()生成密码哈希,登录时用password_verify()验证,绝对禁止明文存储密码
四、数据库结构确认

根据你提供的截图,确保:

  • 用户表(user)包含user_id(主键)、user_username、user_password字段
  • 会话表(sessions)包含sessions_id(主键)、sessions_userid(关联用户表user_id)、sessions_token、sessions_serial、sessions_created(时间类型字段)

内容的提问来源于stack exchange,提问作者Mridushyamal Barman

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 10:18:44