如何在PHP中更改会话超时?


Answers:


324

如果需要严格的保证,会话超时是必须在代码中实现的概念。这是唯一的办法,你可以绝对肯定的是,没有永远的会话将不活动的X分钟后存活。

如果稍微放宽此要求是可以接受的,并且可以设置下限而不是严格的持续时间,则可以轻松完成此操作,而无需编写自定义逻辑。

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

如果您的会话与饼干(他们可能是)来实现,并且如果客户端是没有恶意的,你可以设定一个上限通过调整某些参数绑定在会话持续时间。如果您将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,其有效期限将是不确定的。这是因为生存期已在服务器端终止的会话不会立即被垃圾回收,而是仅在会话GC启动时才进行垃圾回收。

    GC是一个潜在的昂贵流程,因此通常概率很小或什至为零(获得大量点击的网站可能会完全放弃概率性GC,并安排其每隔X分钟在后台进行一次)。在这两种情况下(假定不合作的客户端),有效会话生存期的下限将是session.gc_maxlifetime,但是上限将是不可预测的。

  • 如果未将session.gc_maxlifetime时间跨度设置为同一时间,则服务器可能会在此之前更早地丢弃空闲会话数据;在这种情况下,仍会记住其会话ID的客户端将显示该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对您很重要的情况下,必须注意session_regenerate_id在需要时使用它们重新生成它们。


问题:如果打电话给我们,让我们每分钟说一次,会增加限制吗?例如在10:00我叫它,所以它的限制是11:00,在1分钟10:01之后,限制是11:01吗?
oneofakind 2014年

@oneofakind:如果您打电话给什么呢?
2014年

1
这些是:ini_set('session.gc_maxlifetime',3600); session_set_cookie_params(3600);
oneofakind 2014年

@oneofakind:是的,但前提是您也必须同时调用session_start()(否则完全无效),并且仅当您始终之前调用这两个选项session_start(否则gc_maxlifetime才有可能影响当前打开的所有会话,而session_set_cookie_params只能影响以开头的新会话)当前请求)。
2014年

@Jon如果我再次调用session_start(),是否会重置$ _SESSION中的所有内容?如果您的意思是“有可能影响所有会话”,怎么办?谢谢回复。
oneofakind 2014年

33

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

例如,在Debian Linux系统中,session.gc_probability=0默认情况下通过在配置中进行设置来禁用PHP的内部垃圾收集,而是通过/etc/cron.d/php来完成,该文件运行在XX:09和XX:39(即,每半小时)。此cron作业将查找早于配置中指定的session.gc_maxlifetime的会话,如果找到,则将其删除。结果,在这些系统中ini_set('session.gc_maxlifetime', ...)被忽略。这也解释了为什么出现此问题:PHP会话超时太快,OP在一个主机中有问题,但是在切换到另一台主机时问题不再存在。

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

可用的替代方法包括:

  1. 按照PHP中的指定,在PHP中设置其他会话(保存)处理程序以将您的会话保存到其他目录或数据库中:自定义会话处理程序(PHP手册),以便cron作业不会到达它,而只有PHP可以内部垃圾收集发生。此选项可能可以利用它ini_set()来设置session.gc_maxlifetime,但我更喜欢忽略回调中的maxlifetime参数,gc()并自行确定最大寿命。

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

文档中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)

@Oli:粗读后看起来正确。您可能还想看看 stackoverflow.com/questions/520237/…,但是如果您无权访问php.ini您的实际选择,将受到严格限制。
2013年

另外,在Ubuntu 14上,似乎/usr/lib/php5/maxlifetime无法计算24分钟以下的值。因此,您不能将会话超时设置为低于此值。
亨利

“完全无需考虑PHP内部会话处理,并实现自己的会话管理。” 天哪,这是危险的建议。不可避免地会导致安全噩梦。
Kzqai

@Kzqai我还注意到“它需要更多的代码,因此存在更多的bug和安全漏洞的机会”。这不是建议,我在列举替代方法,但是如果您有改进建议,请这样做。
Pedro Gimeno

3

为使用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


3

放入$_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()在恶意客户端或机器人的情况下,直接破坏会话而不是依赖重定向可能是更好的选择。


2

只是有关共享托管服务器的通知或已添加到域的通知=

为了使设置生效,您必须使用php_value session.save_path“ folderA / sessionsA”为添加的域设置不同的保存会话目录。

因此,请在您的根服务器上创建一个文件夹,而不要在public_html中创建一个文件夹,并且不要从外部进行公开访问。对于我的cpanel /服务器,该文件夹的权限为0700,工作正常。请尝试...

  • PHP代码 =

     #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);
     ini_set('session.cache_expire', 57600);
     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


1

否。如果您无权访问php.ini,则不能保证所做的更改会产生任何影响。

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


嗨,Col,我一直在寻找这个地方,以便找到与您联系的方式。我看到您在上个关闭的帖子(星期日)上给了我一些建议。我忙于另一个项目,现在不见了。我真的很想尝试您的建议。反正那里找到你写的东西?
老狗

据我所知,它不仅已关闭,而且已删除。这些人没有荣誉。是的,您的问题是我正在讨论的通用解决方案。我会通过电子邮件给您写信。简而言之,它是关于另外运行两个查询以获取这些上一个/下一个值的。SELECT id FROM gallery WHERE SortOrder > $currentsortorder LIMIT 1
您的常识

0

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


4
-1:session.gc_maxlifetime不是控制会话生存期的设置。如果将设置session.gc_divisor1,则可能会像这样工作,这太糟糕了,但这太可怕了。
乔恩

1
@Jon我在SO上看到了很多答案,这表明相反的情况,为什么呢?stackoverflow.com/questions/514155/... stackoverflow.com/questions/9904105/...
giannis christofakis

2
@yannishristofakis:gc_maxlifetime设置会话数据有资格进行垃圾回收的时间间隔-如果在经过了这么长的时间后发生GC,则会话数据将被销毁(默认设置与会话过期相同)。但是GC会在每次会话开始时随机触发,因此不能保证该会话实际上会到期-您可以绘制概率与时间的关系曲线,但看起来不会像是一堵砖墙。那只是冰山一角。参见stackoverflow.com/questions/520237/…–
乔恩(Jon)
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.