【问题标题】:How can i stop an AJAX call keeping a PHP Session alive我如何停止 AJAX 调用以保持 PHP 会话处于活动状态
【发布时间】:2011-10-09 04:46:44
【问题描述】:

我的网站上有一个使用 CakePHP 的身份验证系统。它为此使用 PHP 会话。

我有一个 AJAX 调用(在每分钟运行的 setInterval 内)到一个检查用户是否仍然登录的函数。如果它返回 false,那么 Javascript 将获取当前 URL 并尝试重定向它们,然后将它们重定向到登录页面。从理论上讲,这是可行的,因为它主动要求用户重新登录,而不是保持陈旧的会话,后者只会要求他们在单击某些内容时立即登录。我的问题是我的 AJAX 调用使会话保持活动状态。所以永远不要退出(我们不希望这样)

我可以在 CakePHP 中做些什么或者我可以使用任何其他方法来阻止这种情况发生吗?

【问题讨论】:

  • 我也在寻找解决这个问题的方法。我所做的是在页面加载后立即调用 setTimeout(),等待会话时间,然后重定向到注销页面。我知道这不是一个好主意,这就是为什么我有兴趣在这里获得解决方案
  • 请参阅下面关于 setInterval() 行为的回答

标签: javascript ajax cakephp php


【解决方案1】:

这里我导出了一个用于检测会话是否处于活动状态的解决方案。

不需要任何开销.. 我们可以检测并重定向正常 http 请求的会话超时。 就像在 ajax 中一样,我们应该以不同的方式对待它,有一些逻辑。

#第1步(在服务器端)

在 php 方面。 首先创建一个身份验证函数,该函数必须在所有页面的第一行调用此身份验证。

最好在 config.php 文件或 auth.php 文件中添加此 auth() 函数,并在会话创建后包含在所有 php 文件中。

包括'config/auth.php';

// in auth.php file copy and past the following lines 

      function auth() {
        if (!isset($_SESSION['USER_ID'])) {
        //  checking whether the request is ajax or not. Ajax requests are xml http request 
            if (isset($_SERVER['HTTP_X_REQUESTED_WITH']) && !empty($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest') {
               // set a status code manually. 418 is unused http code
                header("HTTP/1.0 418 Session Timeout", TRUE);
               //  return or send a json response with status string and location to redirect
                echo json_encode(array(
                    "status" => "time_out",
                    "location" => BASE_URL . "login.php?message=Session Timeout! Please log in and Try again.",
                ));
                exit;
            }
       // whether the request is normal http request do the redirection from the server side
            if (basename($_SERVER['PHP_SELF']) != 'login.php') {
                header("Location:" . BASE_URL . "login.php");
            }
        } }

#第2步(在客户端)

这里我使用 jQuery 创建一个 js 文件,该文件必须在项目中的所有 html( 模板 ) 页面中调用。

最好包含在commen页眉或页脚中

创建一个 ajax 设置

$.ajaxSetup({
    statusCode: {
        418: function (respose) {
            // $.parseJSON() can avoid by specifying dataType:"json" in ajax parameters
            var jsonObject = $.parseJSON(respose.responseText);
            if (jsonObject.status == "time_out") {
                window.location = jsonObject.location;
            }
        }
    }
});

祝你有美好的一天......

【讨论】:

    【解决方案2】:

    这并不能完全回答您提出的问题,但我认为这是我想分享的最佳实践。

    setInterval() 实际上并不像您期望的那样运行。这并不意味着您的代码每分钟都会执行一次,它只是确保您的代码每分钟都会被添加到队列中。

    这样做的问题是 计时器代码可能无法完成执行 在代码再次添加到队列之前。结果将是 计时器代码连续运行多次,没有多少时间 它们之间。幸运的是,JavaScript 引擎足够聪明,可以 避免这个问题。使用 setInterval() 时,将计时器代码添加到 仅当没有计时器代码的其他实例时才进入队列 已经在队列中。这确保了添加之间的时间 队列的计时器代码至少是指定的时间间隔。

    这种重复计时器规则的缺点是双重的:(1) 间隔可以被跳过,并且(2)间隔可以小于 预计在多个计时器代码执行之间...... -N。 Zakas,面向 Web 开发人员的专业 JavaScript

    解决这个问题的方法是像这样格式化你的计时器代码:

    setTimeout( function() {
    
           // code to be run
    
           setTimeout( arguments.callee, *interval* );
    
    }, *interval* );
    

    注意:arguments.callee 不能在严格模式下使用。

    【讨论】:

    • 请注意,arguments.callee 将在严格模式下抛出 TypeError 异常(参见 ES5 §10.6 注释 3)。此外,该功能不是“每分钟”添加的,它是在上次运行后 1 分钟添加一次,这就是它不一致的原因。按照您的建议使用 setTimeout 将有效地具有相同的行为,它不能解决任何问题。在 MDN 上进行了更长的讨论:https://developer.mozilla.org/en-US/docs/DOM/window.setInterval
    • 我已经注意到了,为什么还要重复呢?你知道我的意思...实际上,它确实解决了问题。它解决了跳过间隔的问题,因为超时已经在队列中。只有在代码完成运行后才会将其添加到队列中。
    • 如果您滚动到您发布的链接的底部,RobG,它会为您提供与我发布的完全相同的代码,唯一不同的是它使用命名函数来避免 TypeError...任何称职的 JavaScript 开发人员都将能够弄清楚如何在具有命名函数的严格环境中使用上述代码。
    【解决方案3】:

    当您检查会话的有效性时,将&ajax=(任何内容)添加到查询字符串中。

    然后,更改您的 PHP 会话代码:

    session_start();
    if(!isset($_GET["ajax"])) $_SESSION["lastactivity"] = time();
    if(now() - $_SESSION["lastactivity"] > 3600){ //3600 seconds
        header("Location: login.php?url="+urlencode(str_replace("&ajax=", "", $_SERVER["REQUEST_URI"])));
        //Example:
        // Location: login.php?url=/sensible/secret.php?mode=show&hide=nothing
        exit;
    }
    

    【讨论】:

      【解决方案4】:

      我认为您不能仅依靠 Auth 会话来完成此操作。

      我要做的是在用户表上创建一个新字段来跟踪上次活动,例如 last_activity 作为 mysql 时间戳。然后在您的AppController::beforeFilter() 中,将此字段设置为使用当前日期时间进行更新(因此每个请求都会发生),但如果是您发出请求的 ping 操作,请将其设置为跳过此字段(您可以检查$this->here或者甚至可以为其他操作添加您自己的参数)。

      您的 Ajax ping 显然只是读取此字段,如果超过 x 分钟前,您将注销用户。

      【讨论】:

      • 这是正确的,但所有这些都可以在 Session 中完成,而不是每分钟都打扰数据库。
      • 好吧,你去吧,将last_activity存储在会话中,我喜欢将其保存在数据库中,以便查看用户上次在线的时间。
      【解决方案5】:

      正如我在评论中提到的,您可以创建一个包含以下内容的 javascript 文件并在您的所有页面中调用此文件

      setTimeout("checkSession()",1800000);
      function checkSession(){
           //alert("Your session has expired due to inactivity. You will be logged out");
           window.location.reload();//or window.location="logoutAction";
      }
      

      【讨论】:

      • 问题是,这并没有用 PHP 检查会话是否仍然存在。
      • 当页面reloded请求会到服务器,可以检查session。如果页面中有ajax请求,setTimeout计数器应该被重置
      • this guy也在使用类似的方法
      【解决方案6】:

      这样做的明显方法是:

      1. 不要在 Ajax 端点中调用 session_start()
      2. 使用允许您跳过写入部分的后门实现您自己的会话处理程序(例如,基于全局变量的状态或当前 URL)

      作为一个丑陋的黑客,你可以尝试调用 session_id('dummy');或在 Ajax 端点调用 session_start() 后更改会话处理程序。

      【讨论】:

        【解决方案7】:

        您所描述的 AJAX ping 最常用于此目的 - 保持会话处于活动状态。您在会话处于活动状态时访问了您的应用程序这一事实正在刷新它。

        您可以执行以下操作之一:

        • 有一个固定长度的会话,例如30分钟,之后它会 总是过期(不确定这是否是个好主意)并保留 ping
        • 更改逻辑,使您根本不 ping,以及当会话到期和用户 导航到新页面或执行 AJAX 会话,服务器返回适当的状态代码和/或将用户重定向到登录页面。

        我会选择第二个选项。

        【讨论】:

        • 这两点都不符合 OP 的目的。如果用户离开系统很长时间而其他人看着屏幕怎么办。虽然他无法访问任何页面(执行任何服务器操作都会注销会话),但他仍然可以看到页面上当前可用的信息。OP 使用的轮询方法将在会话到期后立即从客户端注销会话跨度>
        • 如果这是 OP 的目的(我不理解它,但可能是这样),那么我建议固定会话并在一段时间不活动后自动注销(例如 10 分钟没有页面上的任何操作)。我只在我的网上银行等超级重要的网站上看到这种行为。如果这确实是 OP 的关注点,我会相应地更新我的答案。
        • 我并不是要批评你。您的第二个选项通常是它的完成方式,但 OP 实际上是试图从客户端使会话无效
        猜你喜欢
        • 2014-06-20
        • 2012-06-05
        • 1970-01-01
        • 2018-11-26
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-03-06
        • 2022-01-23
        相关资源
        最近更新 更多