使用 ajax 强制注销时 db table 未更新

db table not getting updated when forcing logout using ajax

如标题所述,db table trace_users 没有得到更新 当没有 activity 发生 30 分钟 但它得到6 分钟后更新。

下面的代码(JS和PHP)在同一个php文件中mno.php.

我正在通过下面脚本中出现的这一行 let lastActivity = <?php echo time(); ?> ; 存储最后一个 activity 用户。

<?php
session_start();     
if (isset($_GET['action']) && $_GET['action'] == "logout")
{
    session_destroy(); // destroy session data in storage
    !isset($_SESSION['admin']);
    $open = "false";
    $write = "0";
    $stmt = $connect->prepare("UPDATE trace_users SET open=?, write=? WHERE user_name=?");
    $usname = !empty($_SESSION['user_name']) ? $_SESSION['user_name'] : '';
    $stmt->bind_param('sss', $open, $write, $usname);
    $stmt->execute();
}
?>
<script>
    jQuery(document).ready(function($) {

        let lastActivity = <?php echo time(); ?> ;  // storing last activity of user

        let now = <?php echo time(); ?> ;

        let logoutAfter = 360;

        let timer = setInterval(function() {
            now++;
            let delta = now - lastActivity;
            console.log(delta);
            if (delta > logoutAfter) {
                clearInterval(timer);
                //DO AJAX REQUEST TO close.php
                $.ajax({
                    url: "/abc/mno.php",
                    type: 'GET',
                    data: {
                        action: 'logout'
                    }, // Line A
                    success: function(data) {
                        console.log(data); // Line B
                    },
                    error: function(jqXHR, textStatus, errorThrown) {
                        alert(textStatus);
                    }
                });
            }
        }, 1000);
    }); 
</script>

问题陈述:

我想知道我应该在上面的代码中进行哪些更改,以便 当没有 activity 发生 30 分钟(1800 秒)时,数据库 table 也应该得到已更新.

案例 1(不工作):1800 秒(页面注销,数据库不更新)
案例 2(工作):360 秒(页面注销,数据库更新

session.gc_maxlifetime里面的值是1440(Local Value) 1440(Master Value)

这是我的tried/debugged:

在网络选项卡上,当 session 超时设置为 6 分钟60 分钟.

问题是您没有更改任何可以防止会话在 N 秒后过期的内容,您只是以某种方式对脚本进行编码,使其在此时间后销毁会话。会话 gc(垃圾收集器)定期执行并删除旧会话,当发生这种情况时,$_SESSION['LAST_ACTIVITY'] 也将被删除。

您必须尝试阻止 gc 删除会话或更改应用程序中的逻辑。

您需要向 javascript 传递某种永久 UID,以识别用户 即使在会话过期后

为了简化起见,我使用了您的代码中已经存在的 user_name。但是你也可以为每个用户分配一个UUID,这样就无法猜测另一个用户的名字,也无法修改他们的统计信息。


首先,您将 $_SESSION['user_name'] 从 PHP 传递到 JS 闭包。

let userName = "<?php echo $_SESSION['user_name']; ?>";  // don't forget to wrap the JS string value in quotes or apostrophes

然后,您将在 AJAX 请求负载中传递它。

data: {
    action: 'logout',
    user_name: userName  // added param
},

最后,您将覆盖发送到 DB 的值(如果它与负载一起发送)

$usname = !empty($_SESSION['user_name']) ? $_SESSION['user_name'] : '';  // original line
if (isset($_GET['user_name']) && !empty($_GET['user_name'])) {
    $usname = $_GET['user_name'];
}
$stmt->bind_param('sss', $open, $write, $usname);  // original line

完成更新代码:

<?php
if (isset($_GET['action']) && $_GET['action'] == "logout")
{
    session_destroy(); // destroy session data in storage
    !isset($_SESSION['pageadmin']);
    $open = "false";
    $write = "0";
    $stmt = $connect->prepare("UPDATE trace_users SET open=?, write=? WHERE user_name=?");
    $usname = !empty($_SESSION['user_name']) ? $_SESSION['user_name'] : '';
    if (isset($_GET['user_name']) && !empty($_GET['user_name'])) {
        $usname = $_GET['user_name'];
    }
    $stmt->bind_param('sss', $open, $write, $usname);
    $stmt->execute();
}
?>
<script>
    jQuery(document).ready(function($) {

        let lastActivity = <?php echo time(); ?> ;  // storing last activity of user

        let now = <?php echo time(); ?> ;

        let logoutAfter = 10;

        let userName = "<?php echo $_SESSION['user_name']; ?>";

        let timer = setInterval(function() {
            now++;
            let delta = now - lastActivity;
            console.log(delta);
            if (delta > logoutAfter) {
                clearInterval(timer);
                //DO AJAX REQUEST TO close.php
                $.ajax({
                    url: "/abc/mno.php",
                    type: 'GET',
                    data: {
                        action: 'logout',
                        user_name: userName
                    }, // Line A
                    success: function(data) {
                        console.log(data); // Line B
                    },
                    error: function(jqXHR, textStatus, errorThrown) {
                        alert(textStatus);
                    }
                });
            }
        }, 1000);
    }); 
</script>

结尾的几个注释:

  • 将 PHP、HTML 和 JS 一起组合在一个文件中是不好的做法。
  • 您的代码中有些部分不容易重现,我不得不从上下文中猜测(例如,我猜会话已经设置,使用了 jQuery 但没有 <script src="...jquery.js">, 有一个数据库查询但没有单独的 SQL 导入来快速尝试)。请在下一个问题中阅读并应用How to create a Minimal, Reproducible Example中的知识,以便更多的人能够或愿意为您提供帮助。

PHP 进程不会无限期地坐着并且没有程序结构作为循环 ala Node.js 服务器,这将不允许您对会话到期做出反应,因为它不是一个无效的进程它,而是一个与会话关联的简单时间戳,每次您尝试使用它时都会检查它。

我提供的解决方案是一个简单的脚本,它 运行 每 N 分钟执行一次与最后一个用户 activity 时间戳的比较(我假设它是根据该用户的请求更新的)针对有效期(在你的情况下是 30 分钟)。您还可以将会话到期时间设置为 30 分钟,但严格来说这不是必需的。如果时差超过 30 分钟,您将在 table 中更新用户的时间戳,并在必要时使他们的会话无效。该脚本可以 运行 通过 cron 或其替代方案,并遍历执行检查所需的所有用户。

当用户在服务器上注销但客户端不知道并可能继续发送注销请求时不要忘记处理这种情况 - 引发警告框相当不清楚(最好是 return HTTP 未经授权的代码并以不同方式处理它 - 例如重定向到登录屏幕)

您需要解决两件事。

1).将 ajax 请求设置为 async:false.

$.ajax({
    url: "/abc/mno.php",
      type: 'GET',
      async: false,   //  <---- ADDED ASYNC FALSE
      data: {
        action: 'logout'
    }, // Line A
     success: function(data) {
        console.log(data); // Line B
      },
     error: function(jqXHR, textStatus, errorThrown) {
         alert(textStatus);
       }
 });

2).执行 SQL 查询后销毁会话。

<?php
session_start();     
if (isset($_GET['action']) && $_GET['action'] == "logout")
{
    !isset($_SESSION['pageadmin']);
    $open = "false";
    $write = "0";
    $stmt = $connect->prepare("UPDATE trace_users SET open=?, write=? WHERE user_name=?");
    $usname = !empty($_SESSION['user_name']) ? $_SESSION['user_name'] : '';
    $stmt->bind_param('sss', $open, $write, $usname);
    $stmt->execute();
    session_destroy(); // destroy session data in storage <---- DESTORY AT THE END
}
?>