如何实现单用户专属页面的单并发访问限制?
实现单用户专属页面并发访问限制方案
一、数据库表设计
先在MariaDB中创建会话记录表,用于跟踪页面的活跃访问状态:
CREATE TABLE `page_sessions` ( `id` int(11) NOT NULL AUTO_INCREMENT, `user_id` int(11) NOT NULL COMMENT '对应用户专属页面的userID', `session_token` varchar(64) NOT NULL COMMENT '当前访问会话的唯一标识', `last_active_time` int(11) NOT NULL COMMENT '最后活跃时间戳', `is_active` tinyint(1) NOT NULL DEFAULT 1 COMMENT '会话是否活跃', PRIMARY KEY (`id`), UNIQUE KEY `user_id_active` (`user_id`, `is_active`) COMMENT '确保同一用户只有一个活跃会话' ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
这个唯一键user_id_active可直接防止同一用户同时存在多个活跃会话,避免并发写入冲突。
二、页面加载时的初始校验(PHP)
当用户访问专属页面(如https://website.com/userID-456/name-surname),PHP先解析URL中的user_id,再校验是否已有活跃会话:
<?php // 从URL中提取userID,示例:从userID-456提取456 $urlSegments = explode('/', $_SERVER['REQUEST_URI']); $userSegment = $urlSegments[1]; // 假设userID-xxx是URL的第二个分段 preg_match('/userID-(\d+)/', $userSegment, $matches); $userId = $matches[1] ?? 0; if (!$userId) { header('Location: /error-page'); exit; } // 连接数据库(替换为你的实际数据库连接逻辑) $db = new mysqli('localhost', 'username', 'password', 'database_name'); if ($db->connect_error) { die('数据库连接失败'); } // 生成当前会话的唯一标识token $sessionToken = md5(uniqid(mt_rand(), true)); $currentTime = time(); // 尝试插入新活跃会话,利用唯一键冲突判断是否已有活跃会话 $sql = "INSERT INTO page_sessions (user_id, session_token, last_active_time) VALUES (?, ?, ?)"; $stmt = $db->prepare($sql); $stmt->bind_param('isi', $userId, $sessionToken, $currentTime); if (!$stmt->execute()) { // 唯一键冲突,说明已有其他设备在访问 echo '<script> alert("Your link was accessed on other device and this page will close"); setTimeout(() => { window.location.href = "/other-page"; }, 2000); </script>'; exit; } // 将token传给前端,用于后续心跳更新 echo '<input type="hidden" id="sessionToken" value="' . $sessionToken . '">'; echo '<input type="hidden" id="userId" value="' . $userId . '">'; ?>
三、前端定时心跳(JS/jQuery)
前端每隔1分钟发送请求到后端,更新当前会话的活跃时间,维持会话有效性:
$(document).ready(function() { const sessionToken = $('#sessionToken').val(); const userId = $('#userId').val(); const checkInterval = 60000; // 1分钟间隔 // 定时发送心跳请求 setInterval(function() { $.ajax({ url: '/update-session.php', method: 'POST', data: { user_id: userId, session_token: sessionToken }, success: function(response) { // 会话失效则提示并跳转 if (response.status === 'invalid') { alert("Your link was accessed on other device and this page will close"); window.location.href = "/other-page"; } }, error: function() { // 请求失败可添加降级提示逻辑 } }); }, checkInterval); });
四、后端心跳更新接口(update-session.php)
处理前端心跳请求,更新会话活跃时间并校验会话有效性:
<?php $userId = $_POST['user_id'] ?? 0; $sessionToken = $_POST['session_token'] ?? ''; $db = new mysqli('localhost', 'username', 'password', 'database_name'); if ($db->connect_error) { die(json_encode(['status' => 'error'])); } $currentTime = time(); // 更新当前会话的活跃时间,同时校验token匹配 $sql = "UPDATE page_sessions SET last_active_time = ? WHERE user_id = ? AND session_token = ? AND is_active = 1"; $stmt = $db->prepare($sql); $stmt->bind_param('iii', $currentTime, $userId, $sessionToken); $stmt->execute(); if ($stmt->affected_rows === 0) { // 会话不存在或已失效 echo json_encode(['status' => 'invalid']); } else { echo json_encode(['status' => 'ok']); } ?>
五、定时清理失效会话(Cronjob)
设置Cronjob每隔2分钟清理超过1.5分钟未活跃的会话,释放页面访问权限:
# 编辑Cronjob crontab -e # 添加以下内容(替换为你的清理脚本实际路径) */2 * * * * php /var/www/html/clean-sessions.php
对应的清理脚本clean-sessions.php:
<?php $db = new mysqli('localhost', 'username', 'password', 'database_name'); if ($db->connect_error) { die('数据库连接失败'); } // 清理超过90秒未活跃的会话 $expireTime = time() - 90; $sql = "UPDATE page_sessions SET is_active = 0 WHERE last_active_time < ? AND is_active = 1"; $stmt = $db->prepare($sql); $stmt->bind_param('i', $expireTime); $stmt->execute(); ?>
补充说明
- 数据库唯一键
user_id_active从底层避免了并发写入问题,确保同一时刻只有一个活跃会话。 - 心跳间隔和清理阈值可按需调整,比如心跳1分钟、清理阈值设为2分钟,给临时断网用户留缓冲时间。
- 用户主动关闭页面后,会话会在定时清理任务执行后标记为失效,其他用户即可访问该页面。
内容的提问来源于stack exchange,提问作者kold-kreator
相关产品推荐
相关产品推荐

