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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/8311320/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-26 04:24:15  来源:igfitidea点击:

How to change the session timeout in PHP?

phpsessionsession-timeout

提问by Oli

I would like to extend the session timeout in php

我想在 php 中延长会话超时时间

I know that it is possible to do so by modifying the php.ini file. But I don't have access to it.

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

So is it possible to do it only with php code?

那么是否可以仅使用 php 代码来完成?

回答by Jon

Session timeout is a notion that has to be implemented in code if you want strict guarantees; that's the only wayyou can be absolutely certain that no session ever will survive after X minutes of inactivity.

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

If relaxing this requirement a little is acceptable and you are fine with placing a lower boundinstead of a strict limit to the duration, you can do so easily and without writing custom logic.

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

Convenience in relaxed environments: how and why

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

Ifyour sessions are implemented with cookies (which they probably are), and ifthe clients are not malicious, you can set an upper bound on the session duration by tweaking certain parameters. If you are using PHP's default session handling with cookies, setting session.gc_maxlifetimealong with session_set_cookie_paramsshould work for you like this:

如果您的会话是使用 cookie(它们可能是)实现的,并且如果客户端不是恶意的,您可以通过调整某些参数来设置会话持续时间的上限。如果您使用 PHP 的默认会话处理 cookie,设置session.gc_maxlifetimewithsession_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!

This works by configuring the server to keep session data around for at least one hour of inactivity and instructing your clients that they should "forget" their session id after the same time span. Both of these steps are required to achieve the expected result.

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

  • If you don't tell the clients to forget their session id after an hour (or if the clients are malicious and choose to ignore your instructions) they will keep using the same session id and its effective duration will be non-deterministic. That is because sessions whose lifetime has expired on the server side are not garbage-collected immediately but only whenever the session GC kicks in.

    GC is a potentially expensive process, so typically the probability is rather small or even zero (a website getting huge numbers of hits will probably forgo probabilistic GC entirely and schedule it to happen in the background every X minutes). In both cases (assuming non-cooperating clients) the lower bound for effective session lifetimes will be session.gc_maxlifetime, but the upper bound will be unpredictable.

  • If you don't set session.gc_maxlifetimeto the same time span then the server might discard idle session data earlier than that; in this case, a client that still remembers their session id will present it but the server will find no data associated with that session, effectively behaving as if the session had just started.

  • 如果您在一小时后不告诉客户端忘记他们的会话 ID(或者如果客户端是恶意的并选择忽略您的指令),他们将继续使用相同的会话 ID,其有效持续时间将是不确定的。那是因为在服务器端已经过期的会话不会立即被垃圾回收,而只是在会话 GC 开始时才会被回收。

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

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

Certainty in critical environments

关键环境中的确定性

You can make things completely controllable by using custom logic to also place an upper boundon session inactivity; together with the lower bound from above this results in a strict setting.

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

Do this by saving the upper bound together with the rest of the session data:

通过将上限与其余会话数据一起保存来执行此操作:

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;

Session id persistence

会话 ID 持久化

So far we have not been concerned at all with the exact values of each session id, only with the requirement that the data should exist as long as we need them to. Be aware that in the (unlikely) case that session ids matter to you, care must be taken to regenerate them with session_regenerate_idwhen required.

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

回答by Pedro Gimeno

If you use PHP's default session handling, the only way to reliably change the session duration in all platforms is to change php.ini. That's because in some platforms, garbage collection is implemented through a script that runs every certain time (a cronscript) that reads directly from php.ini, and therefore any attempts at changing it at run time, e.g. via ini_set(), are unreliable and most likely won't work.

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

For example, in Debian Linux systems, PHP's internal garbage collection is disabled by setting session.gc_probability=0by default in the configuration, and is instead done via /etc/cron.d/php, which runs at XX:09 and XX:39 (that is, every half hour). This cron job looks for sessions older than the session.gc_maxlifetimespecified in the configuration, and if any are found, they are deleted. As a consequence, in these systems ini_set('session.gc_maxlifetime', ...)is ignored. That also explains why in this question: PHP sessions timing out too quickly, the OP had problems in one host but the problems ceased when switching to a different host.

例如,在 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 会话超时太快,OP 在一台主机上出现问题,但在切换到另一台主机时问题停止了。

So, given that you don't have access to php.ini, if you want to do it portably, using the default session handling is not an option. Apparently, extending the cookie lifetime was enough for your host, but if you want a solution that works reliably even if you switch hosts, you have to use a different alternative.

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

Available alternative methods include:

可用的替代方法包括:

  1. Set a different session (save) handler in PHP to save your sessions in a different directory or in a database, as specified in PHP: Custom Session Handlers (PHP manual), so that the cronjob doesn't reach it, and only PHP's internal garbage collection takes place. This option probably can make use of ini_set()to set session.gc_maxlifetimebut I prefer to just ignore the maxlifetimeparameter in my gc()callback and determine maximum lifetime on my own.

  2. Completely forget about PHP internal session handling and implement your own session management. This method has two main disadvantages: you will need your own global session variables, so you lose the advantage of the $_SESSIONsuperglobal, and it needs more code thus there are more opportunities for bugs and security flaws. Most importantly, the session identifier should be generated out of cryptographically secure random or pseudorandom numbers to avoid session ID predictability (leading to possible session hiHymaning), and that is not so easy to do with PHP portably. The main advantage is that it will work consistently in all platforms and you have full control over the code. That's the approach taken e.g. by the phpBBforum software (at least version 1; I'm not sure about more recent versions).

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

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

There is an example of (1) in the documentation for session_set_save_handler(). The example is long but I'll reproduce it here, with the relevant modifications necessary to extend the session duration. Note the inclusion of session_set_cookie_params()to increase the cookie lifetime as well.

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

Approach (2) is more complicated; basically, you have to re-implement all session functions on your own. I won't go into details here.

方法(2)比较复杂;基本上,您必须自己重新实现所有会话功能。我不会在这里详细介绍。

回答by mohamadRezaSamadi

Put $_SESSION['login_time'] = time();into the previous authentication page. And the snipped below in every other page where you want to check the session time-out.

放入$_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.
}

Edit :This only works if you already used the tweaks in other posts, or disabled Garbage Collection, and want to manually check the session duration. Don't forget to add die()after a redirect, because some scripts/robots might ignore it. Also, directly destroying the session with session_destroy()instead of relying on a redirect for that might be a better option, again, in case of a malicious client or a robot.

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

回答by Neil Walden

Adding comment for anyone using Plesk having issues with any of the above as it was driving me crazy, setting session.gc_maxlifetime from your PHP script wont work as Plesk has it's own garbage collection script run from cron.

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

I used the solution posted on the link below of moving the cron job from hourly to daily to avoid this issue, then the top answer above should work:

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

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

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

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

回答by ChriStef

Just a notice for a sharing hostingserver or added on domains =

只是一个共享托管服务器的通知或添加到域=

For your settings to work you must have a different save session dir for added domain by using php_value session.save_path "folderA/sessionsA".

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

So create a folder to your root server, not into the public_html and not to be publicity accessed from outside. For my cpanel/server worked fine the folder permissions 0700. Give a try...

所以创建一个文件夹到你的根服务器,不要进入public_html,也不要从外部公开访问。对于我的 cpanel/服务器,文件夹权限 0700 工作正常。试一试...

  • php code=

     #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');
    
  • 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');
    

before session_start();

在 session_start() 之前;

or

或者

  • .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
    
  • .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
    

After many researching and testing this worked fine for shared cpanel/php7 server. Many thanks to: NoiS

经过多次研究和测试,这适用于共享 cpanel/php7 服务器。非常感谢:NoiS

回答by Your Common Sense

No. If you don't have access to the php.ini, you can't guarantee that changes would have any effect.

不可以。如果您无权访问 php.ini,则不能保证更改会产生任何效果。

I doubt you need to extend your sessions time though.
It has pretty sensible timeout at the moment and there are no reasons to extend it.

我怀疑你需要延长你的会议时间。
目前它有相当合理的超时时间,没有理由延长它。

回答by Nathan Q

You can override values in php.ini from your PHP code using ini_set().

您可以使用ini_set().