会话超时提醒必须由前端定时检测+后端配合实现,不能依赖session.gc_maxlifetime自动触发;需手动维护$_SESSION['last_activity'],前端轮询接口获取剩余时间并分级提醒,超时后前后端同步清理会话状态。

会话超时提醒不能靠 session.gc_maxlifetime 自动触发
PHP 的会话垃圾回收是被动、概率性触发的,session.gc_maxlifetime 只控制服务端会话数据保留时长,**不会在到期前通知用户**,更不会主动弹窗或跳转。所谓“提醒”,必须由前端定时检测 + 后端配合判断来实现。
典型错误是把 $_SESSION['last_activity'] 更新逻辑写在 session_start() 之后但没做刷新,导致时间戳停滞;或前端轮询接口时没校验响应状态,误以为会话还有效。
- 每次有效请求(如 AJAX、页面加载)都需更新
$_SESSION['last_activity'] = time() - 前端轮询间隔应小于服务端超时阈值(例如后端设 1800 秒,前端每 60 秒查一次)
- 轮询接口必须返回明确的状态码或字段(如
{"valid": true, "remaining": 120}),不能只靠 HTTP 状态码
用 $_SESSION['last_activity'] 手动维护活跃时间
PHP 默认不记录最后操作时间,得自己存。关键是更新时机和作用域:必须在用户真实产生交互(非静态资源请求、非跨域预检)时更新,且不能在登录前就写入(否则未登录用户也能触发 session 文件创建)。
示例代码片段(放在公共入口或中间件中):
立即学习“PHP免费学习笔记(深入)”;
if (session_status() === PHP_SESSION_ACTIVE && isset($_SESSION['user_id'])) {
$_SESSION['last_activity'] = time();
}
注意:session_start() 必须已调用,且该逻辑不应出现在登录接口本身(避免登录失败也更新时间)。
- 不要在
session_set_save_handler()中覆盖写入逻辑,容易冲突 - 如果用了 Redis 存 session,确保
last_activity被序列化进 value,而不是被忽略 - 更新时间后无需调用
session_write_close(),除非你确定后续无其他 session 写操作
前端用 fetch() 轮询会话剩余时间并弹窗提醒
轮询不是为了“续命”,而是为了提前告知用户。建议分两级提醒:比如剩余 120 秒时显示倒计时 banner,剩余 30 秒弹确认框,超时后跳转登录页。
关键点在于处理响应异常:网络中断、500 错误、跨域拒绝,这些都应视为会话可能已失效,直接导向登录页,而不是静默重试。
- 轮询请求必须带
credentials: 'include',否则 cookie 不发送 - 响应体里不要只返回布尔值,至少包含
remaining字段,方便前端计算精确倒计时 - 倒计时用
setTimeout而非setInterval,避免因 JS 阻塞或页面失焦导致误差累积
超时后强制登出要清除客户端敏感状态
后端销毁 session($_SESSION = [] + session_destroy())只是第一步。前端若还留着 JWT token、用户昵称、权限菜单,就会出现“页面还能点但接口全 401”的混乱状态。
最稳妥的做法是:后端返回 {"status": "expired"} 后,前端执行完整清理:
localStorage.removeItem('auth_token');
sessionStorage.removeItem('user_menu');
document.cookie = 'PHPSESSID=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/;';
window.location.href = '/login?expired=1';
特别注意 Cookie 清除路径(path=/)要和服务端 setcookie 一致,否则删不干净;若用了 SameSite=Strict,跳转登录页时可能带不过 cookie,得靠后端重定向而非前端 window.location。



















