You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何实现单用户专属页面的单并发访问限制?

实现单用户专属页面并发访问限制方案

一、数据库表设计

先在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.28 00:47:37