【问题标题】:How to change the session timeout in PHP?如何更改 PHP 中的会话超时?
【发布时间】:2012-01-08 19:49:00
【问题描述】:

我想在php中延长会话超时

我知道可以通过修改 php.ini 文件来做到这一点。 但我无权访问它。

那么是否可以只用php代码来做呢?

【问题讨论】:

标签: php session session-timeout


【解决方案1】:

如果你想要严格的保证,会话超时是一个必须在代码中实现的概念;这是唯一的方法,您可以绝对确定在 X 分钟不活动后不会有任何会话存活。

如果稍微放宽这一要求是可以接受的,并且您可以设置下限而不是对持续时间进行严格限制,那么您可以轻松地做到这一点,而无需编写自定义逻辑。

轻松环境中的便利性:如何以及为什么

如果您的会话是使用 cookie 实现的(它们可能是),并且如果客户端不是恶意的,您可以通过以下方式设置会话持续时间的上限调整某些参数。如果您使用 PHP 的默认会话处理和 cookie,设置 session.gc_maxlifetimesession_set_cookie_params 应该适合您:

// server should keep session data for AT LEAST 1 hour
ini_set('session.gc_maxlifetime', 3600);

// each client should remember their session id for EXACTLY 1 hour
session_set_cookie_params(3600);

session_start(); // ready to go!

这通过配置服务器以保持会话数据至少一小时不活动并指示您的客户端他们应该在相同的时间跨度后“忘记”他们的会话 ID 来工作。 这两个步骤都是实现预期结果所必需的。

  • 如果您不告诉客户在一小时后忘记他们的会话 ID(或者如果客户是恶意的并选择忽略您的指示),他们将继续使用相同的会话 ID,其有效持续时间将是不确定的.这是因为在服务器端过期的会话不会立即被垃圾收集,而只会被whenever the session GC kicks in

    GC 是一个潜在的昂贵过程,因此概率通常很小甚至为零(获得大量点击的网站可能会完全放弃概率 GC,并安排它每 X 分钟在后台发生一次)。在这两种情况下(假设非合作客户端),有效会话生命周期的下限都是session.gc_maxlifetime,但上限是不可预测的。

  • 如果您没有将session.gc_maxlifetime 设置为相同的时间跨度,那么服务器可能会更早地丢弃空闲会话数据;在这种情况下,仍然记得其会话 ID 的客户端将显示它,但服务器将找不到与该会话关联的数据,实际上就像会话刚刚开始一样。

关键环境中的确定性

您可以通过使用自定义逻辑来使事情完全可控,同时在会话不活动上放置一个上限;加上上面的下限,这导致了严格的设置。

通过将上限与会话数据的其余部分一起保存来做到这一点:

session_start(); // ready to go!

$now = time();
if (isset($_SESSION['discard_after']) && $now > $_SESSION['discard_after']) {
    // this session has worn out its welcome; kill it and start a brand new one
    session_unset();
    session_destroy();
    session_start();
}

// either new or old, it should live at most for another hour
$_SESSION['discard_after'] = $now + 3600;

会话 id 持久性

到目前为止,我们根本不关心每个会话 id 的确切值,只关心数据只要我们需要就应该存在的要求。请注意,在(不太可能)会话 ID 对您很重要的情况下,必须小心在需要时使用 session_regenerate_id 重新生成它们。

【讨论】:

  • 问题:如果调用这个,让我们每分钟说一次,它会增加它的限制吗?例如在 10:00 我打电话给它,所以它的限制是 11:00,1 分钟后,10:01,限制是 11:01?
  • @oneofakind:如果你叫什么?
  • 这些:ini_set('session.gc_maxlifetime', 3600); session_set_cookie_params(3600);
  • @oneofakind:是的,但前提是你也调用了session_start()(否则根本没有效果)并且只有你总是session_start之前调用这两个(否则gc_maxlifetime 有可能影响当前打开的所有会话,而session_set_cookie_params 只能影响以当前请求开始的新会话。
  • @Jon 如果我再次调用 session_start() 会重置我的 $_SESSION 中的所有内容吗?如果您的意思是“有可能影响所有会话”,那又如何呢?感谢您的回复。
【解决方案2】:

如果您使用 PHP 的默认会话处理,在所有平台上可靠地更改会话持续时间的唯一方法是更改​​ php.ini。这是因为在某些平台上,垃圾收集是通过一个每隔特定时间运行的脚本(一个 cron 脚本)来实现的,该脚本直接从 php.ini 读取,因此任何尝试在运行时更改它,例如通过ini_set(),是不可靠的,很可能不会工作。

例如,在 Debian Linux 系统中,PHP 的内部垃圾收集通过在配置中默认设置 session.gc_probability=0 来禁用,而是通过 /etc/cron.d/php 来完成,它运行在 XX:09 和 XX :39(即每半小时一次)。此 cron 作业查找早于配置中指定的 session.gc_maxlifetime 的会话,如果找到,则将其删除。因此,在这些系统中,ini_set('session.gc_maxlifetime', ...) 被忽略。这也解释了为什么在这个问题中:PHP sessions timing out too quickly,OP 在一台主机上出现问题,但在切换到另一台主机时问题就消失了。

因此,鉴于您无权访问 php.ini,如果您想以便携方式进行操作,则不能选择使用默认会话处理。显然,延长 cookie 的生命周期对您的主机来说已经足够了,但如果您想要一个即使切换主机也能可靠运行的解决方案,您必须使用不同的替代方案。

可用的替代方法包括:

  1. 在 PHP 中设置不同的会话(保存)处理程序以将会话保存在不同的目录或数据库中,如 PHP: Custom Session Handlers (PHP manual) 中指定的那样,以便 cron 作业不会没有到达它,只有 PHP 的内部垃圾收集发生。此选项可能可以利用ini_set() 设置session.gc_maxlifetime,但我更喜欢忽略gc() 回调中的maxlifetime 参数并确定我的最大生命周期拥有。

  2. 完全忘记 PHP 内部会话处理并实现您自己的会话管理。这种方法有两个主要缺点:您需要自己的全局会话变量,因此您失去了$_SESSION 超全局的优势,并且它需要更多的代码,因此存在更多错误和安全漏洞的机会。最重要的是,会话标识符应该由密码安全的随机数或伪随机数生成,以避免会话 ID 的可预测性(导致可能的会话劫持),而这在 PHP 便携版中并不容易做到。主要优点是它将在所有平台上始终如一地工作,并且您可以完全控制代码。这就是采取的方法,例如由 phpBB 论坛软件(至少版本 1;我不确定是否有更新的版本)。

documentation for session_set_save_handler() 中有 (1) 的示例。该示例很长,但我将在此处重现它,并进行必要的相关修改以延长会话持续时间。请注意包含 session_set_cookie_params() 以增加 cookie 的生命周期。

<?php
class FileSessionHandler
{

    private $savePath;
    private $lifetime;

    function open($savePath, $sessionName)
    {
        $this->savePath = 'my_savepath'; // Ignore savepath and use our own to keep it safe from automatic GC
        $this->lifetime = 3600; // 1 hour minimum session duration
        if (!is_dir($this->savePath)) {
            mkdir($this->savePath, 0777);
        }

        return true;
    }

    function close()
    {
        return true;
    }

    function read($id)
    {
        return (string)@file_get_contents("$this->savePath/sess_$id");
    }

    function write($id, $data)
    {
        return file_put_contents("$this->savePath/sess_$id", $data) === false ? false : true;
    }

    function destroy($id)
    {
        $file = "$this->savePath/sess_$id";
        if (file_exists($file)) {
            unlink($file);
        }

        return true;
    }

    function gc($maxlifetime)
    {
        foreach (glob("$this->savePath/sess_*") as $file) {
            if (filemtime($file) + $this->lifetime < time() && file_exists($file)) { // Use our own lifetime
                unlink($file);
            }
        }

        return true;
    }
}

$handler = new FileSessionHandler();
session_set_save_handler(
    array($handler, 'open'),
    array($handler, 'close'),
    array($handler, 'read'),
    array($handler, 'write'),
    array($handler, 'destroy'),
    array($handler, 'gc')
    );

// the following prevents unexpected effects when using objects as save handlers
register_shutdown_function('session_write_close');

session_set_cookie_params(3600); // Set session cookie duration to 1 hour
session_start();
// proceed to set and retrieve values by key from $_SESSION

方法(2)更复杂;基本上,您必须自己重新实现所有会话功能。此处不再赘述。

【讨论】:

  • 有人可以确认吗?
  • @Oli:粗略阅读后看起来是正确的。您可能还想查看stackoverflow.com/questions/520237/…,但如果您无权访问php.ini,您的实际选择将受到严格限制。
  • 另外,在 Ubuntu 14 上,/usr/lib/php5/maxlifetime 似乎不会计算低于 24 分钟的值。因此,您不能将会话超时设置为低于此值。
  • "完全忘记 PHP 内部会话处理并实现您自己的会话管理。"天哪,这是危险的建议。不可避免地会导致一场安全噩梦。
  • @Kzqai 我还注意到“它需要更多代码,因此有更多机会出现错误和安全漏洞”。这不是建议,我在列举替代方案,但如果您有改进建议,请执行。
【解决方案3】:

共享主机服务器或添加到域中的通知 =

要使您的设置正常工作,您必须使用 php_value session.save_path folderA/sessionsA 为添加的域使用不同的保存会话目录。

因此,为您的根服务器创建一个文件夹,而不是在public_html 中,也不要从外部公开访问。对于我的 cpanel/server,文件夹权限 0700 工作正常。试一试……

# Session timeout, 2628000 sec = 1 month, 604800 = 1 week, 57600 = 16 hours, 86400 = 1 day
ini_set('session.save_path', '/home/server/.folderA_sessionsA');
ini_set('session.gc_maxlifetime', 57600); 
ini_set('session.cookie_lifetime', 57600);
# session.cache_expire is in minutes unlike the other settings above         
ini_set('session.cache_expire', 960);
ini_set('session.name', 'MyDomainA');

session_start();之前

或将其放入您的 .htaccess 文件中。

php_value session.save_path /home/server/.folderA_sessionsA
php_value session.gc_maxlifetime 57600
php_value session.cookie_lifetime 57600
php_value session.cache_expire 57600
php_value session.name MyDomainA

经过多次研究和测试,这对于共享 cpanel/php7 服务器运行良好。非常感谢:NoiS

【讨论】:

  • 我只需要按照你所说的更改 save_path,它在我的共享托管服务器上工作
【解决方案4】:

为使用 Plesk 的任何人添加评论,因为这让我发疯,从您的 PHP 脚本设置 session.gc_maxlifetime 将不起作用,因为 Plesk 有自己的垃圾收集脚本从 cron 运行。

我使用下面链接中发布的解决方案,将 cron 作业从每小时移动到每天以避免此问题,那么上面的最佳答案应该可以工作:

mv /etc/cron.hourly/plesk-php-cleanuper /etc/cron.daily/

https://websavers.ca/plesk-php-sessions-timing-earlier-expected

【讨论】:

    【解决方案5】:

    $_SESSION['login_time'] = time();放入上一个认证页面。 并在您要检查会话超时的每个其他页面中进行以下截图。

    if(time() - $_SESSION['login_time'] >= 1800){
        session_destroy(); // destroy session.
        header("Location: logout.php");
        die(); // See https://thedailywtf.com/articles/WellIntentioned-Destruction
        //redirect if the page is inactive for 30 minutes
    }
    else {        
       $_SESSION['login_time'] = time();
       // update 'login_time' to the last time a page containing this code was accessed.
    }
    

    编辑:这仅适用于您已经在其他帖子中使用了这些调整,或者禁用了垃圾收集,并且想要手动检查会话持续时间。 不要忘记在重定向后添加die(),因为某些脚本/机器人可能会忽略它。此外,如果遇到恶意客户端或机器人,直接使用session_destroy() 销毁会话而不是依赖重定向可能是更好的选择。

    【讨论】:

      【解决方案6】:

      没有。 如果您无权访问 php.ini,则无法保证更改会产生任何影响。

      不过,我怀疑您是否需要延长会话时间。
      目前它有相当合理的超时,没有理由延长它。

      【讨论】:

      • 嗨,Col,我一直在寻找与您联系的方式。我看到你给了我一些建议,关于我上一个关闭的帖子(周日)。我忙于另一个项目,现在它已经消失了。我真的很想试试你的建议。无论如何,这都可以找到您写的内容?
      • 据我所知,它不仅被关闭,而且被删除。这些人没有尊严。是的,您的问题有我所说的通用解决方案。我会通过电子邮件给你写信。简而言之,它是关于运行 2 个额外的查询来获取这些 prev/next 值。 SELECT id FROM gallery WHERE SortOrder &gt; $currentsortorder LIMIT 1
      • 正如我们在其他答案中指出的那样,实际上有解决方案。也有充分的理由将会话保留更长时间(例如存储视图设置)。
      • @jor 你肯定把会话与 cookie(或数据库)混淆了
      • @YourCommonSense 会话基于 cookie(但不安全的 url-param 方式除外)。
      【解决方案7】:

      您可以使用 ini_set() 从您的 PHP 代码中覆盖 php.ini 中的值。

      【讨论】:

      • -1: session.gc_maxlifetime 不是控制会话生命周期的设置。如果你将session.gc_divisor 设置为1,它可能会像这样工作,但这太可怕了。
      • @Jon 我在 SO 上看到了很多答案,建议相反,这是为什么呢? stackoverflow.com/questions/514155/…stackoverflow.com/questions/9904105/…
      • @yannishristofakis:gc_maxlifetime 设置会话数据符合垃圾收集条件的时间间隔——如果在经过这么长时间后发生 GC,会话数据将被销毁(默认设置为与过期会话相同)。但是 GC 在每次会话开始时以概率方式触发,因此不能保证会话实际上会过期——您可以绘制概率与时间的曲线,但它看起来不像砖墙。这只是冰山一角;见stackoverflow.com/questions/520237/…
      • 这仅适用于您在主机上拥有一个会话管理(即一个网站)的情况。否则最短垃圾回收超时规则关于其他人。
      猜你喜欢
      • 1970-01-01
      • 2017-01-02
      • 2012-09-27
      • 1970-01-01
      • 2018-09-23
      • 1970-01-01
      相关资源
      最近更新 更多