HTTP Сесія

Вступ

Оскільки HTTP-застосунки не зберігають стан, сесії надають спосіб зберігати інформацію про користувача між кількома запитами. Ця інформація про користувача зазвичай розміщується в постійному сховищі / бекенді, до якого можна отримати доступ з наступних запитів.

Laravel постачається з різноманітними бекендами сесій, до яких можна отримати доступ через виразний, уніфікований API. Підтримка популярних бекендів, таких як Memcached, Redis та бази даних, включена.

Конфігурація

Файл конфігурації сесії вашого застосунку зберігається у config/session.php. Обов'язково перегляньте доступні вам параметри в цьому файлі. За замовчуванням Laravel налаштований на використання драйвера сесії database.

Опція конфігурації driver сесії визначає, де будуть зберігатися дані сесії для кожного запиту. Laravel включає різноманітні драйвери:

  • file - сесії зберігаються в storage/framework/sessions.
  • cookie - сесії зберігаються в безпечних, зашифрованих cookies.
  • database - сесії зберігаються в реляційній базі даних.
  • memcached / redis - сесії зберігаються в одному з цих швидких сховищ на основі кешу.
  • dynamodb - сесії зберігаються в AWS DynamoDB.
  • array - сесії зберігаються в масиві PHP і не будуть збережені.

Драйвер масиву в основному використовується під час тестування і запобігає збереженню даних у сесії.

Попередні вимоги до драйвера

База даних

Коли ви використовуєте драйвер сесій database, вам потрібно переконатися, що у вас є таблиця бази даних для зберігання даних сесії. Зазвичай, це включено в стандартну 0001_01_01_000000_create_users_table.php міграцію бази даних Laravel; однак, якщо з якоїсь причини у вас немає таблиці sessions, ви можете скористатися командою Artisan make:session-table для створення цієї міграції:

php artisan make:session-table
 
php artisan migrate

Redis

Перш ніж використовувати Redis сесії з Laravel, вам потрібно або встановити PHP-розширення PhpRedis через PECL, або встановити пакет predis/predis (~1.0) через компонувальник. Для отримання додаткової інформації про налаштування Redis, зверніться до документації Redis Laravel.

Змінна середовища SESSION_CONNECTION або опція connection у конфігураційному файлі session.php можуть бути використані для вказівки, яке з'єднання Redis використовується для зберігання сесій.

Взаємодія з сесією

Отримання даних

Існує два основних способи роботи з даними сесії в Laravel: глобальний хелпер session та через екземпляр Request. Спочатку розглянемо доступ до сесії через екземпляр Request, який може бути вказаний у замиканні маршруту або методі контролера. Пам'ятайте, що залежності методу контролера автоматично впроваджуються через Laravel Сервіс-контейнер:

<?php
 
namespace App\Http\Controllers;
 
use Illuminate\Http\Request;
use Illuminate\View\View;
 
class UserController extends Controller
{
/**
* Показати профіль вказаного користувача.
*/
public function show(Request $request, string $id): View
{
$value = $request->session()->get('key');
 
// ...
 
$user = $this->users->find($id);
 
return view('user.profile', ['user' => $user]);
}
}

Коли ви отримуєте елемент із сесії, ви також можете передати значення за замовчуванням як другий аргумент методу get. Це значення за замовчуванням буде повернуто, якщо вказаний ключ не існує в сесії. Якщо ви передасте замикання як значення за замовчуванням до методу get і запитуваний ключ не існує, замикання буде виконано і його результат буде повернуто:

$value = $request->session()->get('key', 'default');
 
$value = $request->session()->get('key', function () {
    return 'default';
});

Глобальний Хелпер session

Ви також можете використовувати глобальну PHP-функцію session для отримання та зберігання даних у сесії. Коли хелпер session викликається з одним аргументом-рядком, він поверне значення цього ключа сесії. Коли хелпер викликається з масивом пар ключ/значення, ці значення будуть збережені у сесії:

Route::get('/home', function () {
    // Отримати дані з сесії...
    $value = session('key');
 
    // Вказування значення за замовчуванням...
    $value = session('key', 'default');
 
    // Зберегти дані в сесії...
    session(['key' => 'value']);
});

Мало практичної різниці між використанням сесії через екземпляр HTTP-запиту та використанням глобального хелпера session. Обидва методи є тестованими за допомогою методу assertSessionHas, який доступний у всіх ваших тестових випадках.

Отримання всіх даних сесії

Якщо ви хочете отримати всі дані в сесії, ви можете використовувати метод all:

$data = $request->session()->all();

Отримання Частини Даних Сесії

Методи only та except можуть бути використані для отримання підмножини даних сесії:

$data = $request->session()->only(['username', 'email']);
 
$data = $request->session()->except(['username', 'email']);

Визначення, чи існує елемент у сесії

Щоб визначити, чи присутній елемент у сесії, ви можете використовувати метод has. Метод has повертає true, якщо елемент присутній і не є null:

if ($request->session()->has('users')) {
    // ...
}

Щоб визначити, чи присутній елемент у сесії, навіть якщо його значення є null, ви можете використовувати метод exists:

if ($request->session()->exists('users')) {
    // ...
}

Щоб визначити, чи відсутній елемент у сесії, ви можете використовувати метод missing. Метод missing повертає true, якщо елемент відсутній:

if ($request->session()->missing('users')) {
    // ...
}

Зберігання даних

Щоб зберегти дані в сесії, зазвичай ви будете використовувати метод put екземпляра запиту або глобальний хелпер session:

// Через екземпляр запиту...
$request->session()->put('key', 'value');
 
// За допомогою глобального хелпера "session"...
session(['key' => 'value']);

Додавання значень до масиву сесії

Метод push може бути використаний для додавання нового значення до значення сесії, яке є масивом. Наприклад, якщо ключ user.teams містить масив назв команд, ви можете додати нове значення до масиву таким чином:

$request->session()->push('user.teams', 'developers');

Отримання та Видалення Елемента

Метод pull отримає та видалить елемент із сесії в одному виразі:

$value = $request->session()->pull('key', 'default');

Збільшення та зменшення значень сесії

Якщо ваші дані сесії містять ціле число, яке ви бажаєте збільшити або зменшити, ви можете використовувати методи increment та decrement:

$request->session()->increment('count');
 
$request->session()->increment('count', $incrementBy = 2);
 
$request->session()->decrement('count');
 
$request->session()->decrement('count', $decrementBy = 2);

Тимчасові дані (Flash Data)

Іноді може виникнути потреба зберегти дані в сесії для наступного запиту. Ви можете зробити це за допомогою методу flash. Дані, збережені в сесії за допомогою цього методу, будуть доступні негайно, а також під час наступного HTTP-запиту. Після цього запиту тимчасові дані буде видалено. Тимчасові дані найчастіше використовуються для короткочасних повідомлень про статус:

$request->session()->flash('status', 'Task was successful!');

Якщо вам потрібно зберегти ваші flash-дані для кількох запитів, ви можете використовувати метод reflash, який збереже всі flash-дані для додаткового запиту. Якщо вам потрібно зберегти лише конкретні flash-дані, ви можете використовувати метод keep:

$request->session()->reflash();
 
$request->session()->keep(['username', 'email']);

Щоб зберегти ваші flash-дані лише для поточного запиту, ви можете використовувати метод now:

$request->session()->now('status', 'Task was successful!');

Видалення даних

Метод forget видалить частину даних із сесії. Якщо ви хочете видалити всі дані з сесії, ви можете використовувати метод flush:

// Забути один ключ...
$request->session()->forget('name');
 
// Забути кілька ключів...
$request->session()->forget(['name', 'status']);
 
$request->session()->flush();

Регенерація ідентифікатора сесії

Регенерація ідентифікатора сесії часто виконується для запобігання зловмисникам від експлуатації атаки фіксації сесії на ваш застосунок.

Laravel автоматично регенерує ID сесії під час автентифікації, якщо ви використовуєте один з стартових наборів застосунків Laravel або Laravel Fortify; однак, якщо вам потрібно вручну регенерувати ID сесії, ви можете використовувати метод regenerate:

$request->session()->regenerate();

Якщо вам потрібно згенерувати новий ідентифікатор сесії та видалити всі дані з сесії в одному виразі, ви можете використовувати метод invalidate:

$request->session()->invalidate();

Блокування сесії

Щоб використовувати блокування сесій, ваш застосунок повинен використовувати драйвер кешу, який підтримує атомарні блокування. Наразі ці драйвери кешу включають memcached, dynamodb, redis, mongodb (включено в офіційний пакет mongodb/laravel-mongodb), database, file та array драйвери. Крім того, ви не можете використовувати cookie драйвер сесій.

За замовчуванням Laravel дозволяє запитам, що використовують ту саму сесію, виконуватися одночасно. Отже, наприклад, якщо ви використовуєте JavaScript HTTP бібліотеку для здійснення двох HTTP запитів до вашого застосунку, вони обидва виконуватимуться одночасно. Для багатьох застосунків це не є проблемою; однак, втрата даних сесії може статися в невеликій частині застосунків, які здійснюють одночасні запити до двох різних кінцевих точок застосунку, які обидві записують дані в сесію.

Щоб пом'якшити це, Laravel надає функціональність, яка дозволяє обмежити одночасні запити для даної сесії. Щоб почати, ви можете просто додати метод block до визначення вашого маршруту. У цьому прикладі вхідний запит до кінцевої точки /profile отримає блокування сесії. Поки це блокування утримується, будь-які вхідні запити до кінцевих точок /profile або /order, які мають той самий ідентифікатор сесії, чекатимуть завершення виконання першого запиту перед тим, як продовжити своє виконання:

Route::post('/profile', function () {
    // ...
})->block($lockSeconds = 10, $waitSeconds = 10);
 
Route::post('/order', function () {
    // ...
})->block($lockSeconds = 10, $waitSeconds = 10);

Метод block приймає два необов'язкові аргументи. Перший аргумент, який приймає метод block, це максимальна кількість секунд, протягом яких блокування сесії має утримуватися перед тим, як воно буде звільнено. Звісно, якщо запит завершить виконання раніше цього часу, блокування буде звільнено раніше.

Другим аргументом, який приймає метод block, є кількість секунд, протягом яких запит повинен чекати, намагаючись отримати блокування сесії. Виняток Illuminate\Contracts\Cache\LockTimeoutException буде викинуто, якщо запит не зможе отримати блокування сесії протягом заданої кількості секунд.

Якщо жоден з цих аргументів не передано, блокування буде отримано на максимальний термін у 10 секунд, а запити чекатимуть максимум 10 секунд, намагаючись отримати блокування:

Route::post('/profile', function () {
    // ...
})->block();

Додавання Користувацьких Драйверів Сесій

Реалізація Драйвера

Якщо жоден з існуючих драйверів сесій не відповідає потребам вашого застосунку, Laravel дозволяє написати власний обробник сесій. Ваш власний драйвер сесій повинен реалізовувати вбудований інтерфейс PHP SessionHandlerInterface. Цей інтерфейс містить лише кілька простих методів. Заготовка реалізації для MongoDB виглядає наступним чином:

<?php
 
namespace App\Extensions;
 
class MongoSessionHandler implements \SessionHandlerInterface
{
    public function open($savePath, $sessionName) {}
    public function close() {}
    public function read($sessionId) {}
    public function write($sessionId, $data) {}
    public function destroy($sessionId) {}
    public function gc($lifetime) {}
}

Оскільки Laravel не включає каталог за замовчуванням для розміщення ваших розширень, ви можете розміщувати їх де завгодно. У цьому прикладі ми створили каталог Extensions для розміщення MongoSessionHandler.

Оскільки призначення цих методів не є одразу зрозумілим, ось огляд призначення кожного методу:

  • Метод open зазвичай використовується в системах зберігання сесій на основі файлів. Оскільки Laravel постачається з драйвером сесій file, вам рідко потрібно буде щось додавати в цей метод. Ви можете просто залишити цей метод порожнім.
  • Метод close, як і метод open, зазвичай можна ігнорувати. Для більшості драйверів він не потрібен.
  • Метод read повинен повертати строкову версію даних сесії, пов'язаних з даним $sessionId. Немає потреби виконувати будь-яку серіалізацію або інше кодування при отриманні або зберіганні даних сесії у вашому драйвері, оскільки Laravel виконає серіалізацію за вас.
  • Метод write повинен записати переданий рядок $data, пов'язаний з $sessionId, до якоїсь системи постійного зберігання, такої як MongoDB або іншої системи зберігання на ваш вибір. Знову ж таки, ви не повинні виконувати жодної серіалізації - Laravel вже подбав про це за вас.
  • Метод destroy повинен видалити дані, пов'язані з $sessionId, з постійного сховища.
  • Метод gc повинен знищити всі дані сесії, які старші за вказаний $lifetime, що є UNIX-міткою часу. Для систем з автоматичним завершенням терміну дії, таких як Memcached та Redis, цей метод може залишатися порожнім.

Реєстрація драйвера

Як тільки ваш драйвер реалізовано, ви готові зареєструвати його в Laravel. Щоб додати додаткові драйвери до бекенду сесій Laravel, ви можете використовувати метод extend, наданий Session фасадом. Ви повинні викликати метод extend з методу boot Сервіс-провайдера. Ви можете зробити це з існуючого App\Providers\AppServiceProvider або створити абсолютно новий провайдер:

<?php
 
namespace App\Providers;
 
use App\Extensions\MongoSessionHandler;
use Illuminate\Contracts\Foundation\Application;
use Illuminate\Support\Facades\Session;
use Illuminate\Support\ServiceProvider;
 
class SessionServiceProvider extends ServiceProvider
{
/**
* Зареєструвати будь-які сервіси застосунку.
*/
public function register(): void
{
// ...
}
 
/**
* Ініціалізуйте будь-які сервіси застосунку.
*/
public function boot(): void
{
Session::extend('mongo', function (Application $app) {
// Повернути реалізацію SessionHandlerInterface...
return new MongoSessionHandler;
});
}
}

Після того як драйвер сесії було зареєстровано, ви можете вказати драйвер mongo як драйвер сесії вашого застосунку, використовуючи змінну середовища SESSION_DRIVER або в файлі конфігурації застосунку config/session.php.