Avatar ·

Как завершить сессию PHP через 30 минут неактивности пользователя?

Привет, коллеги! Я новичок в PHP и разрабатываю сайт, где пользователи авторизуются. У меня есть простая система сессий: после логина я сохраняю данные в `$_SESSION`. Но я заметил, что сессия не завершается, даже если пользователь закрыл браузер и вернулся через час — он всё ещё авторизован. Мне нужно, чтобы сессия автоматически истекала через 30 минут после последнего действия пользователя (не с момента входа, а именно после последнего клика или запроса). Я читал про параметры `session.gc_maxlifetime` и `session.cookie_lifetime`, но они работают не так, как я ожидаю. Например, я установил: ```php ini_set('session.gc_maxlifetime', 1800); ini_set('session.cookie_lifetime', 1800); session_start(); ``` Но это, кажется, влияет только на время жизни файла сессии на сервере и куки в браузере, а не на активность пользователя. Если пользователь просто не закрывает браузер, сессия может жить дольше, чем нужно, из-за того, что сборщик мусора срабатывает не сразу. Как правильно реализовать таймаут неактивности? Мне нужно, чтобы при каждом запросе проверялось, прошло ли 30 минут с последнего действия, и если да — сессия уничтожалась. Желательно с примером кода. Заранее спасибо за помощь! <<>>

Решение: проверка времени последнего действия вручную

Параметры session.gc_maxlifetime и session.cookie_lifetime действительно не гарантируют точное истечение сессии по времени неактивности. Первый отвечает за время жизни файла сессии на сервере, но сборщик мусора (GC) запускается только с вероятностью 1% при каждом запросе (по умолчанию). Второй — за время жизни куки в браузере, но это не отменяет данные сессии на сервере.

Правильный подход — хранить в сессии метку времени последнего действия пользователя и проверять её при каждом запросе. Вот полный пример реализации:

<?php
// Настройка времени жизни сессии (30 минут = 1800 секунд)
$timeout = 1800; // 30 минут

// Запуск сессии
session_start();

// Функция для проверки и обновления времени активности
function checkSessionTimeout($timeout) {
    // Если в сессии есть метка времени последнего действия
    if (isset($_SESSION['last_activity'])) {
        // Вычисляем разницу между текущим временем и последним действием
        $inactive_time = time() - $_SESSION['last_activity'];
        
        // Если прошло больше, чем $timeout секунд
        if ($inactive_time > $timeout) {
            // Уничтожаем сессию
            session_unset();     // Очищаем все переменные сессии
            session_destroy();   // Уничтожаем сессию
            return true;         // Возвращаем true, если сессия истекла
        }
    }
    
    // Обновляем время последнего действия
    $_SESSION['last_activity'] = time();
    return false; // Сессия активна
}

// Вызываем проверку при каждом запросе
$session_expired = checkSessionTimeout($timeout);

if ($session_expired) {
    // Перенаправляем на страницу логина или выводим сообщение
    header('Location: login.php?expired=1');
    exit;
}

// Остальной код страницы (только если сессия активна)
?>

Как это работает?

  • Хранение метки времени: При каждом успешном запросе мы сохраняем в $_SESSION['last_activity'] текущий timestamp с помощью time().
  • Проверка при каждом запросе: В начале скрипта вызывается checkSessionTimeout(). Если разница между текущим временем и last_activity превышает 30 минут, сессия уничтожается.
  • Автоматическое продление: Если пользователь активен (делает запросы), метка времени обновляется, и таймер сбрасывается.

Дополнительные рекомендации

Для надёжности можно также настроить параметры сессии, чтобы сборщик мусора работал эффективнее:

// В начале скрипта, до session_start()
ini_set('session.gc_maxlifetime', $timeout);
ini_set('session.gc_probability', 1);
ini_set('session.gc_divisor', 1); // Запускать GC при каждом запросе (не рекомендуется на продакшене без необходимости)
session_start();

Однако основной механизм — это именно ручная проверка времени. Не полагайтесь только на GC.

Важно: Не используйте session_regenerate_id() без необходимости, если не хотите сбрасывать таймер. Если используете, убедитесь, что обновляете last_activity после регенерации.

<<>>

Альтернативный подход: использование куки с временем жизни и проверка на сервере

Можно комбинировать куки и серверную проверку. Идея: установить куку с временем жизни 30 минут, а на сервере проверять её наличие. Если кука истекла или удалена, сессия считается недействительной. Однако этот метод менее надёжен, так как пользователь может удалить куки вручную.

<?php
session_start();

// Устанавливаем куку на 30 минут (1800 секунд) при первом запросе
if (!isset($_COOKIE['session_timer'])) {
    setcookie('session_timer', time(), time() + 1800, '/');
}

// При каждом запросе проверяем, есть ли кука
if (!isset($_COOKIE['session_timer'])) {
    // Кука отсутствует или истекла — уничтожаем сессию
    session_destroy();
    header('Location: login.php');
    exit;
} else {
    // Обновляем куку (продлеваем на 30 минут)
    setcookie('session_timer', time(), time() + 1800, '/');
}
?>

Недостатки: Куки могут быть отключены в браузере, и этот метод не защищает от подделки времени. Ручная проверка на сервере (как в первом ответе) — более надёжный и безопасный способ.

Используйте первый подход, если вам нужна гарантированная защита от неактивности.