PHP:如何检测会话是否已自动过期?

16

当定义了会话过期时间时,是否可以在其过期时调用事件监听器?

如果不可能,是否可以运行cron作业来检查会话是否已过期?

注意:我想要一个服务器解决方案。我谈论的是当定义了会话过期时间并且会话因为该期限自动完成时的情况。


@Goufalite 服务器 - tirenweb
你想调用一个PHP函数吗?如果你在服务器上这样做,超时时间将会是当用户尝试转到另一个PHP页面时。也许你想包含一些jQuery来加载一个PHP脚本,以便重定向用户和完成其他需要的操作。 - Teddy Codes
2
PHP不是真正的事件驱动的。任何发生的事情都需要在请求/响应周期中发生。 - apokryfos
@ziiweb 如果您需要帮助,能否详细说明问题? - Teddy Codes
请检查这个链接,或许能对你有所帮助:http://stackoverflow.com/questions/31609397/session-time-out-warning-in-javascript-with-jquery-dialog/38762822#38762822 - Jigarb1992
显示剩余9条评论
13个回答

18
是否可以在事件监听器过期时调用它?简短回答是不行。关于会话,有一些奇特的细节人们往往忽略。使用默认处理程序时,会话并不在gc_maxlifetime结束时过期-这只是数据变得符合自动删除条件的时间。没有会话所有者触发随后删除会话的任何操作-这是其他人会话的副作用。当调用session_write_close()(显式或隐式地由代码结束)时,如果随机数生成器(由相关gc设置调整)产生正确的数字,则PHP将查找超出其TTL的数据并将其删除。如果您想在此时触发操作,则需要打破默认处理程序并应用自己的处理程序。这意味着您不仅必须提供识别和删除会话的机制,还必须确定您的处理程序何时启动以及它应该删除多少个会话-您不希望有runaway process killing your server。在Ubuntu中,session.gc_probability = 0,它是一个cron作业,执行删除陈旧数据文件的功能。您问题中遗漏的非常重要的信息是删除会话的原因。
如果你想防止敏感数据在文件系统中持久存在,那么更好的解决方案是使用仅存储在另一个客户端 cookie 中的密钥来加密数据。这样,您完全消除了在服务器上存储未受保护数据的可能性。(注意,suhosin 的加密会话使用从客户端数据和静态密钥派生的密钥 - 这比随机生成的密钥不安全得多。)这是我之前准备好的一个解决方案。 如果你只是想在 gc_maxlifetime 过期后阻止访问,你应该考虑一个自定义的会话处理程序,将过期的会话视为丢失。也就是说,它并没有真正注销,但是任何后续请求都无法再与会话关联。Stackable 会话处理程序示例中的安全层实现了这样的控制。
OTOH如果您想在事件侦听器中使用会话中的数据,则情况就不同了——您肯定无法使用Suhosin或我的加密来对数据进行加密。但是,更复杂的问题是(默认)数据格式与其他地方使用的格式不同。您需要使用session_encode()和session_decode()。前者只会从$_SESSION数组中读取数据,因此窃听会话需要仔细破坏session_id()session_start()。然而,session_decode()可以轻松转换任何您提供的内容。
在PHP的最新版本中,您可以指定不同的函数对进行序列化/反序列化。
考虑到会话垃圾收集器将读取会话文件中的数据,考虑__wakeup()方法的潜在影响是至关重要的。与此相关的是,处理此类垃圾收集的进程必须在原始PHP进程下运行,该进程创建了会话数据(否则您将拥有特权升级后门)。但是,如果会话存储基质基于文件,那么无论如何都需要这样做。

除了PHP以外,还有其他编程语言编写的会话数据反序列化程序可以防止__wakeup()攻击(尝试谷歌),尽管这可能过度解决问题,而且它们可能没有得到积极维护。如果这是一个问题,那么更合适的解决方案可能是使用WDDX(XML)序列化器,并使用常规XML解析器在GC引擎中读取数据。

如果您正在使用本机处理程序来正常读写数据,但想要实现自己的垃圾回收器,那么您需要编写将session_id映射到文件路径的代码。如果您按照我上面给出的PHP类的链接,您会看到纯PHP实现的几个会话处理程序,包括与本机处理程序兼容的处理程序。

但我强烈建议您在选择在删除时读回会话数据之前,耗尽解决基础问题的每一条途径。


5
你可以使用这个 :)
<? 
session_start();

$time = 3600; // Set expire time with secends.
// Star session here 
if (isset($_SESSION['time']) && (time() - $_SESSION['time']) > $time) {
   // Your Code Here to logout
   header('location: /auth/logout');
   exit();
} else {
  $_SESSION['time'] = time();
 }

4

我在这里做一些假设。

我假设你想要追踪已过期的会话,而且不希望通过数据库或第二个“隐藏”的会话来补充追踪会话数据以确定伪会话何时过期并采取适当措施。

我还假设,在此时防范黑客攻击并不重要。中间人和XSS攻击可以捕获会话ID并将其插入到您的系统中,很难检测到。

这种情况在php中默认由会话cookie处理。我还假设您的会话标识和会话开始也由cookie处理。

实现这种“实时”监测的方式几乎没有,也就是说,保持一个套接字连接到服务器来检测会话过期的时刻。PHP并非为此设计; 安全问题会很多。此外,如果您允许某人通过安全通道远程访问您的服务器,那么为什么一开始要费心使用会话呢?

$sessionCookie = 'myPHPSessCookieName';
$isSessionExpired = false;

if (isset($_COOKIE[$sessionCookie])) {
  // check cookie expiry time to show warnings / near expiry behaviours
} else {
  // handle missing cookie / session
  $isSessionExpired = true;
}

define('SESSION_EXPIRED', $isSessionExpired);    
session_start(['name' => $sessionCookie]);

在启动会话之前,必须检查cookie,否则您可能会得到一个错误的结果。

[编辑:JavaScript解决方案]

如果您想产生一个模拟socket的行为,可以使用javascript。客户端浏览器将使用AJAX请求向服务器发送ping来检查其会话状态,一旦检测到会话已过期,您就可以诱导出一个自己选择的行为。注意:当前状态下的javascript基本上是不安全的。所有与数据和安全有关的工作都应尽可能由服务器完成。

client.php:

<script>
  $(function(){
    var messageElem = $('#selectorForDisplayElementHere');
    var timeoutID = null;

    var checkSession = function(){
      $
        .ajax({/* your ajax params: url, data, dataType, method etc. */})
        .then(function(response){
          messageElem.html(response.html);
          if (response.stop === 1) { 
            clearTimeout(timeoutID);
          }
        });
    };

    var timeoutID = setInterval(checkSession, 5000); // Every 5 seconds
  });
</script>

ajax.php:

$response = ['html' => '', 'stop' => 0];

if (SESSION_EXPIRED) {
  $response ['html'] = '<b>Your session has expired. Please log in again.</b>';
  $response['stop'] = 1;
}

echo(json_encode($response));

这是一个非常简单的处理系统,可以轻松地增强各种功能。它使用普及率很高的jQuery库和标准的php和html/javascript功能。


3

看起来你想在服务器端在特定时间清除会话。你需要做的是定义自己的自定义会话处理程序,并将会话数据放入您完全控制的系统中。因此,假设您将会话处理移动到数据库中,则所有会话数据都完全由您控制。下面是一个缩写的自定义会话处理程序

class Sessions implements \SessionHandlerInterface {
    /**
     * Write Session Data
     * @param string $id Session ID to delete
     * @param string $data Data to write into the session
     */
    public function write($id, $data) {
        $time = time() + 3600; // 1 hour for the sake of example
        $sql = 'REPLACE INTO your_session_table
                SET session_id = ?,
                    data = ?,
                    expires = ?';
        $this->db->query($sql);
        $prep = $this->db->prepare($sql);
        $prep->bind_param('ssi', $id, $data, $time);
        $prep->execute();
    }
}

在这里,您可以运行自己的清理脚本。我建议您使用中间内存存储(memcached, Redis等),这样您就不必每次都从数据库中加载。当您运行清理脚本时,您需要清除它(即拉取所有即将过期的会话并从缓存中删除)。但是,一个简单的SQL语句(例如,每5分钟运行一次的cron作业)可以按需清除会话。

 DELETE FROM your_session_table
 WHERE expires < now();

我没有投反对票,但是你误解了问题。OP并不是在询问如何在一定时间内无活动后使会话过期;OP正在询问如何检查一个会话是否已经过期。这是一个明显的区别。用户已经登录并处于离开状态。在幕后,PHP会使会话过期。屏幕上没有任何变化。OP正在询问如何检测到过期以便采取行动。OP要求提供PHP解决方案,但如果回答同样的问题还能从JavaScript的角度来回答的话(这就是为什么我对这个问题设置了悬赏)就有额外的奖励。 - cssyphus
@gibberish 我猜你想知道为什么它不能被完成的答案。我更倾向于提供一种“有点儿”实现的方法,但没有完全解释清楚。 - Machavity

2
你可以将连接时间存储在某个地方(表格等)以便查看用户连接的时间,并定期检查(cron或服务)是否已经过了特定的持续时间(5分钟等)。但是,如果你想警告用户(文档丢失等),你可能需要在客户端使用Javascript的window.setTimeout。

2
我认为你所需要的可能在这里:访问PHP中的活动会话 简而言之,您需要创建自己的会话处理程序。这是服务器端的操作。
如果您想要添加客户端警报,则可以在JavaScript计时器中存储使用的会话过期时间限制。如果只剩下x分钟,弹出一个消息(可能带有倒计时),向用户显示“您的会话将在 x 分钟后到期:(在此处显示计时器)”。

2
在一个页面上设置初始会话。
例如:Login.php
<?php
session_start();
if(...){
   // success
   $_SESSION['logged_time'] = time();
}
?>

现在在另一个页面检查会话是否已过期。

示例:Index.php

<?php
   session_start();
   $login_session_duration = 60; // 1 minute 

   if($_SESSION['logged_time']){
      if(((time() - $_SESSION['logged_time']) > $login_session_duration)){ 
         echo 'session expired';
         // session will be exired after 1 minutes
      }else{
         echo 'session alive';
      }
   }
?>

2
非常简单的方法是:
获取当前时间。
$current_time=time();

获取在ini文件中设置的会话过期时间。

$maxlifetime = ini_get("session.gc_maxlifetime");

在javascript/query中使用此值,当时间过期时,通过手动计算使用警报通知用户或编写所需的脚本。 或者 您可以将这些日期存储在单独的cookie变量中:
session_expire=1525357483057
但是用户可以更改此变量。

1
每次向服务器发送http请求时,检查会话过期时间。如果会话有效,则进行相关操作,否则重定向到所需位置。
或者,
您可以做一件事来节省时间。创建一个方法来检查当前会话的有效性,并在您的http请求中调用它。

0

如果session_cache_expire()返回0,则说明您的会话已结束。可能需要在操作处理程序上实现此功能。这不会在服务器上设置您的会话,只是解决了客户端浏览器知道何时过期的问题,这就是您的自动检测...

http://php.net/manual/en/function.session-cache-expire.php


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接