30分钟后如何终止PHP会话?


1047

我需要将会话保留30分钟,然后销毁它。


35
请注意,至少两个设置对于设置会话时间至关重要,也许三个。两个绝对重要的参数是session.gc_maxlifetime和session.cookie_lifetime(其中0与一些长数字不同)。为了完全100%地确定允许长时间运行,可能还需要设置session.save_path,这是由于/ tmp目录上默认存储会话文件的操作系统控制的清除时间不同。
2011年

1
我不明白您为什么要终止该会话。如果您担心用户离开计算机而没有注销,并且未经授权的用户接管了他的计算机,则您站点上的会话到期将不会阻止劫机者访问受害者在磁盘上的文件。
Gqqnbig

目前尚不清楚您在这里问什么。您是要实现硬的非活动超时(当前,PHP会很高兴地让您使用会话未超过session.gc_maxlifetime的会话),还是要将该会话限制为30分钟(无论不活动?坦率地说,我认为这里接受的答案对于这两个问题都是不好的建议-在两种情况下,都应该使用自定义会话处理程序来实现该逻辑。
symcbean

Answers:


1663

您应该自己实现会话超时。其他人提到的两个选项(session.gc_maxlifetimesession.cookie_lifetime)都不可靠。我将解释其原因。

第一:

session.gc_maxlifetime
session.gc_maxlifetime指定秒数,此后将数据视为“垃圾”并清除。垃圾收集在会话开始期间发生。

但是,垃圾回收器仅以session.gc_probability除以session.gc_divisor的概率启动。使用这些选项的默认值(分别为1和100),机会只有1%。

好吧,您可以简单地调整这些值,以便更频繁地启动垃圾收集器。但是,当启动垃圾收集器时,它将检查每个已注册会话的有效性。这是高成本的。

此外,当使用PHP的默认session.save_handler文件时,会话数据存储在文件中session.save_path指定的路径中。使用该会话处理程序,会话数据的生存期是根据文件的最后修改日期而不是最后访问日期计算的:

注意:如果使用默认的基于文件的会话处理程序,则文件系统必须跟踪访问时间(atime)。Windows FAT并非如此,如果您卡在FAT文件系统或任何其他无法进行时间跟踪的文件系统上,则您将不得不想出另一种方式来处理会话的垃圾回收。从PHP 4.2.3开始,它使用mtime(修改日期)而不是atime。因此,对于没有时间跟踪功能的文件系统,您不会有任何问题。

因此,还可能会发生以下情况:删除会话数据文件,而由于会话数据最近未更新,因此会话本身仍被视为有效会话。

第二:

session.cookie_lifetime
session.cookie_lifetime指定发送到浏览器的cookie的生存时间(以秒为单位)。[…]

恩,那就对了。这只会影响Cookie的生存期,会话本身可能仍然有效。但是使会话无效是服务器的任务,而不是客户端。因此,这无济于事。实际上,将session.cookie_lifetime设置为0会使会话的cookie成为真正的会话cookie,该cookie仅在关闭浏览器之前才有效。

结论/最佳解决方案:

最好的解决方案是实现自己的会话超时。使用一个简单的时间戳记来表示上一个活动(即请求)的时间,并随每个请求进行更新:

if (isset($_SESSION['LAST_ACTIVITY']) && (time() - $_SESSION['LAST_ACTIVITY'] > 1800)) {
    // last request was more than 30 minutes ago
    session_unset();     // unset $_SESSION variable for the run-time 
    session_destroy();   // destroy session data in storage
}
$_SESSION['LAST_ACTIVITY'] = time(); // update last activity time stamp

在每个请求中更新会话数据也会更改会话文件的修改日期,以便垃圾回收器不会过早删除该会话。

您还可以使用其他时间戳记来定期重新生成会话ID,以避免攻击会话,例如会话固定

if (!isset($_SESSION['CREATED'])) {
    $_SESSION['CREATED'] = time();
} else if (time() - $_SESSION['CREATED'] > 1800) {
    // session started more than 30 minutes ago
    session_regenerate_id(true);    // change session ID for the current session and invalidate old session ID
    $_SESSION['CREATED'] = time();  // update creation time
}

笔记:

  • session.gc_maxlifetime 应至少等于此自定义过期处理程序的生存期(在此示例中为1800);
  • 如果您要在活动 30分钟后(而不是从启动开始 30分钟后)使会话setcookie过期,则还需要使用到期时间time()+60*30来保持会话cookie的活动。

3
如果要检查“非活动时间”,如何更改此设置?换句话说,用户登录后,只要他们继续使用该站点,它就不会注销。但是,如果它们在30分钟内处于非活动状态,它将注销它们吗?
大都会

14
@Metropolis:使用$_SESSION['LAST_ACTIVITY']类似的方法来$_SESSION['CREATED']存储用户上一次活动的时间,但是在每次请求时都更新该值。现在,如果该时间与当前时间的差大于1800秒,则该会话的使用时间已超过30分钟。
Gumbo 2010年

3
@Metropolis:session_unset与相同$_SESSION = array()
浓汤

14
@Gumbo-我有点困惑,您不应该将代码与结合使用ini_set('session.gc-maxlifetime', 1800)吗?否则,至少在ini设置为标准24分钟的情况下,您的会话信息可能会在您仍然认为会话有效的时候被破坏。还是我错过了什么?
jeroen

10
@jeron:是的,你应该。但是请注意,如果使用会话保存处理程序,则session.gc_maxlifetime取决于文件的最后修改日期files。因此,session.gc_maxlifetime应该至少等于此自定义过期处理程序的生存时间。
浓汤

135

PHP会话在30分钟内到期的简单方法。

注意:如果要更改时间,只需将30更改为所需的时间,而不更改* 60:这将给出分钟。


以分钟为单位:(30 * 60)
以天为单位:(n * 24 * 60 * 60)n =天数


Login.php

<?php
    session_start();
?>

<html>
    <form name="form1" method="post">
        <table>
            <tr>
                <td>Username</td>
                <td><input type="text" name="text"></td>
            </tr>
            <tr>
                <td>Password</td>
                <td><input type="password" name="pwd"></td>
            </tr>
            <tr>
                <td><input type="submit" value="SignIn" name="submit"></td>
            </tr>
        </table>
    </form>
</html>

<?php
    if (isset($_POST['submit'])) {
        $v1 = "FirstUser";
        $v2 = "MyPassword";
        $v3 = $_POST['text'];
        $v4 = $_POST['pwd'];
        if ($v1 == $v3 && $v2 == $v4) {
            $_SESSION['luser'] = $v1;
            $_SESSION['start'] = time(); // Taking now logged in time.
            // Ending a session in 30 minutes from the starting time.
            $_SESSION['expire'] = $_SESSION['start'] + (30 * 60);
            header('Location: http://localhost/somefolder/homepage.php');
        } else {
            echo "Please enter the username or password again!";
        }
    }
?>

HomePage.php

<?php
    session_start();

    if (!isset($_SESSION['luser'])) {
        echo "Please Login again";
        echo "<a href='http://localhost/somefolder/login.php'>Click Here to Login</a>";
    }
    else {
        $now = time(); // Checking the time now when home page starts.

        if ($now > $_SESSION['expire']) {
            session_destroy();
            echo "Your session has expired! <a href='http://localhost/somefolder/login.php'>Login here</a>";
        }
        else { //Starting this else one [else1]
?>
            <!-- From here all HTML coding can be done -->
            <html>
                Welcome
                <?php
                    echo $_SESSION['luser'];
                    echo "<a href='http://localhost/somefolder/logout.php'>Log out</a>";
                ?>
            </html>
<?php
        }
    }
?>

LogOut.php

<?php
    session_start();
    session_destroy();
    header('Location: http://localhost/somefolder/login.php');
?>

42
在MVC成为当今标准的当今时代,将逻辑和演示结合起来是不明智的。
2012年

也许我缺少有关会话的基本知识,但是如果操作系统每30分钟破坏一次会话,这有什么用?

25
@stillstanding自言自语[微笑]我将MVC视为可憎的。

2
即使项目很小,只有一个程序员,MVC还是一个好主意吗?我觉得我应该在MVC模型中创建自己的项目(或解决问题,然后再将其创建为MVC),但是由于缺乏MVC的经验,这变成了一个心理障碍:“我如何制作此MVC?” 并分散了需要解决方案的最初目标/问题。
MrVimes '18

@还需提一下的是,在Login.php标题之后在内容发送后发送,这是不好的。
machineaddict

43

这是在设定的时间后注销用户吗?在注册会话时设置会话创建时间(或到期时间),然后检查每个页面加载是否可以处理。

例如:

$_SESSION['example'] = array('foo' => 'bar', 'registered' => time());

// later

if ((time() - $_SESSION['example']['registered']) > (60 * 30)) {
    unset($_SESSION['example']);
}

编辑:我有一种感觉,虽然你的意思是别的。

您可以使用session.gc_maxlifetimeini设置在一定寿命后取消会话:

编辑: ini_set('session.gc_maxlifetime',60 * 30);


1
session.gc-maxlifetime可能是最好的方法。
Powerlord

2
会话cookie生存期存在一些问题,最明显的是,它依赖于客户端来强制执行它。Cookie的生存期可以使客户端清除无用/过期的Cookie,因此请勿将其与任何与安全性相关的混淆。
雅科

gc_maxlifetime还是gc-maxlifetime。它同时支持下划线和连字符吗?
迈克·凯瑟

24

这篇文章展示了控制会话超时的两种方法:http : //bytes.com/topic/php/insights/889606-setting-timeout-php-sessions

恕我直言,第二个选择是一个不错的解决方案:

<?php
/***
 * Starts a session with a specific timeout and a specific GC probability.
 * @param int $timeout The number of seconds until it should time out.
 * @param int $probability The probablity, in int percentage, that the garbage 
 *        collection routine will be triggered right now.
 * @param strint $cookie_domain The domain path for the cookie.
 */
function session_start_timeout($timeout=5, $probability=100, $cookie_domain='/') {
    // Set the max lifetime
    ini_set("session.gc_maxlifetime", $timeout);

    // Set the session cookie to timout
    ini_set("session.cookie_lifetime", $timeout);

    // Change the save path. Sessions stored in teh same path
    // all share the same lifetime; the lowest lifetime will be
    // used for all. Therefore, for this to work, the session
    // must be stored in a directory where only sessions sharing
    // it's lifetime are. Best to just dynamically create on.
    $seperator = strstr(strtoupper(substr(PHP_OS, 0, 3)), "WIN") ? "\\" : "/";
    $path = ini_get("session.save_path") . $seperator . "session_" . $timeout . "sec";
    if(!file_exists($path)) {
        if(!mkdir($path, 600)) {
            trigger_error("Failed to create session save path directory '$path'. Check permissions.", E_USER_ERROR);
        }
    }
    ini_set("session.save_path", $path);

    // Set the chance to trigger the garbage collection.
    ini_set("session.gc_probability", $probability);
    ini_set("session.gc_divisor", 100); // Should always be 100

    // Start the session!
    session_start();

    // Renew the time left until this session times out.
    // If you skip this, the session will time out based
    // on the time when it was created, rather than when
    // it was last used.
    if(isset($_COOKIE[session_name()])) {
        setcookie(session_name(), $_COOKIE[session_name()], time() + $timeout, $cookie_domain);
    }
}

19

好吧,我知道上述答案是正确的,但是它们是在应用程序级别上的,为什么我们不简单地使用.htaccess文件来设置过期时间呢?

<IfModule mod_php5.c>
    #Session timeout
    php_value session.cookie_lifetime 1800
    php_value session.gc_maxlifetime 1800
</IfModule>

1
@Lode的答案给出了完美的解释,为什么不应使用此答案不可靠。
emix

15
if (isSet($_SESSION['started'])){
    if((mktime() - $_SESSION['started'] - 60*30) > 0){
        //Logout, destroy session, etc.
    }
}
else {
    $_SESSION['started'] = mktime();
}


11

使用下面的函数实际上很容易。它使用数据库表名称“ sessions”和字段“ id”和“ time”。

每次用户再次访问您的站点或服务时,都应调用此函数以检查其返回值是否为TRUE。如果为FALSE,则用户已过期并且会话将被破坏(注意:此函数使用数据库类来连接和查询数据库,当然您也可以在函数内部执行该操作或类似的操作):

function session_timeout_ok() {
    global $db;
    $timeout = SESSION_TIMEOUT; //const, e.g. 6 * 60 for 6 minutes
    $ok = false;
    $session_id = session_id();
    $sql = "SELECT time FROM sessions WHERE session_id = '".$session_id."'";
    $rows = $db->query($sql);
    if ($rows === false) {
        //Timestamp could not be read
        $ok = FALSE;
    }
    else {
        //Timestamp was read succesfully
        if (count($rows) > 0) {
            $zeile = $rows[0];
            $time_past = $zeile['time'];
            if ( $timeout + $time_past < time() ) {
                //Time has expired
                session_destroy();
                $sql = "DELETE FROM sessions WHERE session_id = '" . $session_id . "'";
                $affected = $db -> query($sql);
                $ok = FALSE;
            }
            else {
                //Time is okay
                $ok = TRUE;
                $sql = "UPDATE sessions SET time='" . time() . "' WHERE session_id = '" . $session_id . "'";
                $erg = $db -> query($sql);
                if ($erg == false) {
                    //DB error
                }
            }
        }
        else {
            //Session is new, write it to database table sessions
            $sql = "INSERT INTO sessions(session_id,time) VALUES ('".$session_id."','".time()."')";
            $res = $db->query($sql);
            if ($res === FALSE) {
                //Database error
                $ok = false;
            }
            $ok = true;
        }
        return $ok;
    }
    return $ok;
}

9

在会话中存储时间戳


<?php    
$user = $_POST['user_name'];
$pass = $_POST['user_pass'];

require ('db_connection.php');

// Hey, always escape input if necessary!
$result = mysql_query(sprintf("SELECT * FROM accounts WHERE user_Name='%s' AND user_Pass='%s'", mysql_real_escape_string($user), mysql_real_escape_string($pass));

if( mysql_num_rows( $result ) > 0)
{
    $array = mysql_fetch_assoc($result);    

    session_start();
    $_SESSION['user_id'] = $user;
    $_SESSION['login_time'] = time();
    header("Location:loggedin.php");            
}
else
{
    header("Location:login.php");
}
?>

现在,检查时间戳记是否在允许的时间范围内(1800秒为30分钟)

<?php
session_start();
if( !isset( $_SESSION['user_id'] ) || time() - $_SESSION['login_time'] > 1800)
{
    header("Location:login.php");
}
else
{
    // uncomment the next line to refresh the session, so it will expire after thirteen minutes of inactivity, and not thirteen minutes after login
    //$_SESSION['login_time'] = time();
    echo ( "this session is ". $_SESSION['user_id'] );
    //show rest of the page and all other content
}
?>

8

请在包含文件的每页中使用以下代码块。

$expiry = 1800 ;//session expiry required after 30 mins
    if (isset($_SESSION['LAST']) && (time() - $_SESSION['LAST'] > $expiry)) {
        session_unset();
        session_destroy();
    }
    $_SESSION['LAST'] = time();

1

上课30分钟

class Session{
    public static function init(){
        ini_set('session.gc_maxlifetime', 1800) ;
        session_start();
    }
    public static function set($key, $val){
        $_SESSION[$key] =$val;
    }
    public static function get($key){
        if(isset($_SESSION[$key])){
            return $_SESSION[$key];
        } else{
            return false;
        }
    }
    public static function checkSession(){
        self::init();
        if(self::get("adminlogin")==false){
            self::destroy();
            header("Location:login.php");
        }
    }
    public static function checkLogin(){
        self::init();
        if(self::get("adminlogin")==true){
            header("Location:index.php");
        }
    }
    public static function destroy(){
        session_destroy();
        header("Location:login.php");
    }
}

0

正在使用时间戳记...

<?php
if (!isset($_SESSION)) {
    $session = session_start();
} 
if ($session && !isset($_SESSION['login_time'])) {
    if ($session == 1) {
        $_SESSION['login_time']=time();
        echo "Login :".$_SESSION['login_time'];
        echo "<br>";
        $_SESSION['idle_time']=$_SESSION['login_time']+20;
        echo "Session Idle :".$_SESSION['idle_time'];
        echo "<br>";
    } else{
        $_SESSION['login_time']="";
    }
} else {
    if (time()>$_SESSION['idle_time']){
        echo "Session Idle :".$_SESSION['idle_time'];
        echo "<br>";
        echo "Current :".time();
        echo "<br>";
        echo "Session Time Out";
        session_destroy();
        session_unset();
    } else {
        echo "Logged In<br>";
    }
}
?>

我已经用20秒使用timestamp使会话期满

如果需要30分钟,请添加1800(秒为30分钟)...


0

您可以直接使用数据库作为替代。我使用一个称为chk_lgn的数据库函数来完成它。

检查登录检查以查看是否已登录,并在此过程中将检查的日期时间戳记设置为用户的数据库行/列中的最后一个活动日期。

我也在那里检查时间。目前,这对我有用,因为我在每个页面上都使用此功能。

PS我见过的人中没有人提出过一种纯DB解决方案。



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.