如何实现PHP会话超时自动销毁并更新数据库用户在线状态
根因分析
你现有代码的问题在于,PHP是服务端执行的语言,这段超时判断逻辑仅会在用户主动向服务端发起请求时触发。用户直接关闭浏览器后不会再产生任何服务端请求,因此更新数据库状态、销毁会话的逻辑永远不会自动执行。
可行解决方案
方案1:前端心跳机制(实时性最高)
这是最常用的在线状态判断方案,实现逻辑如下:
- 前端页面添加定时器,每隔30秒(可自定义)向服务端发送一次心跳请求,证明用户仍处于活跃状态
- 服务端接收心跳请求后,更新
$_SESSION['Logged']为当前时间,同时给table_admin表新增一个last_activedatetime类型字段,同步更新该字段为当前时间
前端心跳示例代码:
// 每30秒发一次心跳 setInterval(() => { fetch('/heartbeat.php', { method: 'POST', credentials: 'include' // 携带session cookie }) }, 30000)
服务端心跳接口heartbeat.php示例:
<?php session_start(); // 验证用户已登录 if (isset($_SESSION['Logged']) && isset($id)) { // 建议改用预处理语句避免SQL注入 $stmt = mysqli_prepare($this->conexao, "UPDATE table_admin SET last_active = NOW() WHERE IdUser = ?"); mysqli_stmt_bind_param($stmt, "s", $id); mysqli_stmt_execute($stmt); $_SESSION['Logged'] = time(); } ?>
方案2:后端定时任务批量清理
这个方案不需要前端改动,适合对实时性要求不高的场景:
- 同样给
table_admin表新增last_active字段,用户每次发起请求时更新该字段 - 配置系统定时任务:Linux系统可使用crontab,Windows系统使用计划任务,每隔1分钟执行一次清理脚本
- 清理脚本逻辑:扫描所有
StatusAdmin=1且last_active小于当前时间减15分钟的用户,统一设置StatusAdmin=0,同时销毁对应会话
清理脚本示例:
<?php // 连接数据库代码略 $timeout = time() - 900; // 15分钟超时 $stmt = mysqli_prepare($conn, "UPDATE table_admin SET StatusAdmin = 0 WHERE StatusAdmin = 1 AND UNIX_TIMESTAMP(last_active) < ?"); mysqli_stmt_bind_param($stmt, "i", $timeout); mysqli_stmt_execute($stmt); // 可补充删除对应session文件的逻辑 ?>
补充方案:监听页面关闭事件(非100%可靠)
可以监听浏览器beforeunload事件,用户关闭页面时主动发送退出请求,但该方案在浏览器崩溃、强制结束进程、网络异常等场景下不会触发,仅可作为补充:
window.addEventListener('beforeunload', () => { // 使用sendBeacon保证请求发送成功 navigator.sendBeacon('/logout.php'); });
注意事项
原有代码中直接拼接SQL参数$id存在SQL注入风险,建议全部改用预处理语句执行数据库操作。
内容的提问来源于stack exchange,提问作者JM_2021
相关产品推荐
相关产品推荐

