PHP页面刷新Session丢失 多用户登录冲突问题求助
PHP Session 覆盖问题:同一浏览器多页面登录后Session丢失原因及修复方案
核心问题本质
同一浏览器的所有标签页共享同一份Cookie,而PHP默认通过Cookie存储Session ID。当你在新标签页登录另一个用户时,新的Session数据会覆盖原有Session中的id、email等键,导致刷新旧标签页时,Session已被替换成新用户的数据,看起来像是“清除了另一个Session”。
你的代码中存在的关键问题
- 无隔离的Session写入:登录成功后直接覆盖
$_SESSION['id']、$_SESSION['email']等全局Session键,没有做用户Session隔离 - 全表更新SQL漏洞:
UPDATE usertable SET activeLogInDate = '$date'和UPDATE usertable SET activeStatus = '$activeStatus'没有添加WHERE条件,会更新数据库中所有用户的登录日期和在线状态,这是严重的逻辑错误 - 密码验证逻辑错误:用
md5()加密用户输入的密码后,再调用password_verify()验证——password_verify()需要原始明文密码和数据库中password_hash()生成的哈希值,md5()会破坏原始密码,导致验证失效 - SQL注入风险:直接将用户输入的
$email拼接进SQL语句,没有使用预处理语句,存在注入漏洞 - Session冗余存储:在Session中存储用户密码是不必要的,会增加安全风险
修复步骤及代码示例
1. 修复SQL全表更新问题
所有更新用户状态的SQL必须添加WHERE条件,仅更新当前登录用户的数据:
// 替换原登录代码中的更新语句 $sql_insert_date = mysqli_query($conn, "UPDATE usertable SET activeLogInDate = '$date' WHERE email = '$email'"); $sql2 = mysqli_query($conn, "UPDATE usertable SET activeStatus = '$activeStatus' WHERE email = '$email'");
2. 修正密码验证逻辑
去掉md5()加密,直接使用用户输入的原始密码进行验证:
// 替换原密码处理代码 $password = filter_var($_POST['password'], FILTER_SANITIZE_FULL_SPECIAL_CHARS);
3. 解决Session覆盖问题
方案A:强制单用户登录(同一浏览器仅保留一个用户Session)
登录前销毁旧Session,确保新登录的Session是全新的:
// 在登录代码最开头添加 session_start(); session_destroy(); session_unset(); session_start(); // 启动新Session
方案B:支持同一浏览器多用户同时登录
通过自定义Session名称实现隔离,需要配合前端存储Session标识:
- 登录时生成唯一Session名称并传递给前端:
if (password_verify($password, $fetch_pass)) { // 生成唯一Session名称 $session_name = 'user_' . $fetch['id'] . '_' . uniqid(); session_name($session_name); session_start(); $activeStatus = "Online"; $sql2 = mysqli_query($conn, "UPDATE usertable SET activeStatus = '$activeStatus' WHERE email = '$email'"); $_SESSION['id'] = $fetch['id']; $_SESSION['email'] = $email; $_SESSION['username'] = $fetch['username']; // 跳转时携带session_name参数,前端可存在localStorage header("location: /private/private_page/private.php?us=$uniqueID&nm=$username&session=$session_name"); }
- 私有页面加载时,根据传入的Session名称启动对应Session:
// 替换私有页面开头的session_start() if (isset($_GET['session'])) { session_name($_GET['session']); } session_start(); function authenticate() { return array_key_exists('email', $_SESSION); }; if (!authenticate()) { header("location: /private/login/log-in.php"); exit; // 跳转后必须终止脚本执行 } // 从Session获取用户ID,无需重复查询数据库(如果需要最新数据再查询) $user_id = $_SESSION['id']; $email = $_SESSION['email'];
4. 用预处理语句防止SQL注入
以查询用户为例,替换原拼接SQL的方式:
$check_email = "SELECT * FROM usertable WHERE email = ?"; $stmt = mysqli_prepare($conn, $check_email); mysqli_stmt_bind_param($stmt, "s", $email); mysqli_stmt_execute($stmt); $res = mysqli_stmt_get_result($stmt); if (mysqli_num_rows($res) > 0) { $fetch = mysqli_fetch_assoc($res); // 后续逻辑... }
5. 优化Session存储逻辑
移除Session中不必要的密码存储,仅保留用户标识和基础信息:
// 登录成功后仅存必要数据 $_SESSION['id'] = $fetch['id']; $_SESSION['email'] = $email; $_SESSION['username'] = $fetch['username']; // 移除$_SESSION['password']的写入
修正后的完整代码示例
私有页面代码
<?php if (isset($_GET['session'])) { session_name($_GET['session']); } session_start(); function authenticate() { return array_key_exists('email', $_SESSION); }; if (!authenticate()) { header("location: /private/login/log-in.php"); exit; } $user_id = $_SESSION['id']; $email = $_SESSION['email']; // 如需获取用户最新数据,用预处理语句查询 $check_id = "SELECT * FROM usertable WHERE email = ?"; $stmt = mysqli_prepare($conn, $check_id); mysqli_stmt_bind_param($stmt, "s", $email); mysqli_stmt_execute($stmt); $result = mysqli_stmt_get_result($stmt); if (mysqli_num_rows($result) > 0) { $fetch_info = mysqli_fetch_assoc($result); $user_id = $fetch_info['id']; }; ?> <!-- 页面HTML内容 -->
登录页面代码
<?php session_start(); session_destroy(); session_unset(); session_start(); // 若使用多用户登录方案,移除上面的销毁代码,改用自定义Session名称逻辑 $errors = []; if (isset($_POST['login'])) { $email = filter_var($_POST['email'], FILTER_VALIDATE_EMAIL); $password = filter_var($_POST['password'], FILTER_SANITIZE_FULL_SPECIAL_CHARS); date_default_timezone_set("Europe/Rome"); $date = date('Y-m-d H:i:s'); // 预处理语句查询用户 $check_email = "SELECT * FROM usertable WHERE email = ?"; $stmt = mysqli_prepare($conn, $check_email); mysqli_stmt_bind_param($stmt, "s", $email); mysqli_stmt_execute($stmt); $res = mysqli_stmt_get_result($stmt); if (mysqli_num_rows($res) > 0) { $fetch = mysqli_fetch_assoc($res); $fetch_pass = $fetch['password']; if (password_verify($password, $fetch_pass)) { $activeStatus = "Online"; // 更新当前用户在线状态 $sql2 = mysqli_query($conn, "UPDATE usertable SET activeStatus = '$activeStatus', activeLogInDate = '$date' WHERE email = '$email'"); // 生成自定义Session名称(多用户方案用) // $session_name = 'user_' . $fetch['id'] . '_' . uniqid(); // session_name($session_name); // session_start(); $_SESSION['id'] = $fetch['id']; $_SESSION['email'] = $email; $_SESSION['username'] = $fetch['username']; $status = $fetch['status']; if ($status == 'verified') { // 多用户方案跳转时携带session参数 // header("location: /private/private_page/private.php?us={$fetch['uniqueID']}&nm={$fetch['username']}&session=$session_name"); header("location: /private/private_page/private.php?us={$fetch['uniqueID']}&nm={$fetch['username']}"); } else { $info = "Non è stata verificata la tua identità,controlla il messaggio inviato all' email $email"; $_SESSION['info'] = $info; header('location: /private/otp/otp-code.php'); } exit; } else { $errors['email'] = "Inserimento password o email non corretto!"; } } else { $errors['email'] = "Sembra che tu non sia ancora un membro! Clicca sul pulsante registrati per iscriverti."; } }; ?> <!-- 登录表单HTML内容 -->
内容的提问来源于stack exchange,提问作者Andrea
相关产品推荐
相关产品推荐

