使用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
相关产品推荐
相关产品推荐

