Session
Introducción
Dado que las aplicaciones impulsadas por HTTP son sin estado, las sesiones proporcionan una forma de almacenar información sobre el usuario a través de múltiples solicitudes. Esa información del usuario se coloca típicamente en un almacén persistente/backend que puede ser accedido desde solicitudes subsecuentes.
Laravel viene con una variedad de backends de sesión que se acceden a través de una API expresiva y unificada. Se incluye soporte para backends populares como Memcached, Redis y bases de datos.
Configuración
El archivo de configuración de la sesión de tu aplicación se almacena en config/session.php
. Asegúrate de revisar las opciones disponibles en este archivo. Por defecto, Laravel está configurado para usar el driver de sesión de base de datos.
La opción de configuración del driver de sesión define dónde se almacenarán los datos de la sesión para cada solicitud. Laravel incluye una variedad de drivers:
file
– las sesiones se almacenan enstorage/framework/sessions
.cookie
– las sesiones se almacenan en cookies seguras y encriptadas.database
– las sesiones se almacenan en una base de datos relacional.memcached
/redis
– las sesiones se almacenan en uno de estos almacenes rápidos basados en caché.dynamodb
– las sesiones se almacenan en AWS DynamoDB.array
– las sesiones se almacenan en un array de PHP y no se persistirán.
El driver array
se usa principalmente durante las pruebas y evita que los datos almacenados en la sesión se persistan.
Requisitos Previos del Driver
Base de Datos
Al usar el driver de sesión de base de datos, necesitarás asegurarte de tener una tabla de base de datos para contener los datos de la sesión. Típicamente, esto se incluye en la migración de base de datos predeterminada de Laravel 0001_01_01_000000_create_users_table.php
; sin embargo, si por alguna razón no tienes una tabla de sesiones, puedes usar el comando Artisan make:session-table
para generar esta migración:
php artisan make:session-table
php artisan migrate
Redis
Antes de usar sesiones Redis con Laravel, necesitarás instalar la extensión PHP PhpRedis a través de PECL o instalar el paquete predis/predis
(~1.0) a través de Composer. Para más información sobre la configuración de Redis, consulta la documentación de Redis de Laravel.
La variable de entorno SESSION_CONNECTION
, o la opción connection
en el archivo de configuración session.php
, puede usarse para especificar qué conexión Redis se usa para el almacenamiento de sesiones.
Interactuando con la Sesión
Recuperando Datos
Hay dos formas principales de trabajar con datos de sesión en Laravel: el helper global session
y a través de una instancia de Request
. Primero, veamos cómo acceder a la sesión a través de una instancia de Request
, que puede ser indicada en una clausura de ruta o método de controlador. Recuerda, las dependencias del método del controlador se inyectan automáticamente a través del contenedor de servicios de Laravel:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\View\View;
class UserController extends Controller
{
/**
* Mostrar el perfil del usuario dado.
*/
public function show(Request $request, string $id): View
{
$value = $request->session()->get('key');
// ...
$user = $this->users->find($id);
return view('user.profile', ['user' => $user]);
}
}
Cuando recuperas un elemento de la sesión, también puedes pasar un valor predeterminado como segundo argumento al método get
. Este valor predeterminado se devolverá si la clave especificada no existe en la sesión. Si pasas una clausura como valor predeterminado al método get
y la clave solicitada no existe, se ejecutará la clausura y se devolverá su resultado:
$value = $request->session()->get('key', 'default');
$value = $request->session()->get('key', function () {
return 'default';
});
El Helper Global de Sesión
También puedes usar la función PHP global session
para recuperar y almacenar datos en la sesión. Cuando se llama al helper session
con un solo argumento de cadena, devolverá el valor de esa clave de sesión. Cuando se llama al helper con un array de pares clave/valor, esos valores se almacenarán en la sesión:
Route::get('/home', function () {
// Recuperar un dato de la sesión...
$value = session('key');
// Especificar un valor predeterminado...
$value = session('key', 'default');
// Almacenar un dato en la sesión...
session(['key' => 'value']);
});
Hay poca diferencia práctica entre usar la sesión a través de una instancia de solicitud HTTP versus usar el helper global de sesión. Ambos métodos son comprobables a través del método assertSessionHas
que está disponible en todos tus casos de prueba.
Recuperando Todos los Datos de la Sesión
Si deseas recuperar todos los datos en la sesión, puedes usar el método all
:
$data = $request->session()->all();
Recuperando una Parte de los Datos de la Sesión
Los métodos only
y except
pueden usarse para recuperar un subconjunto de los datos de la sesión:
$data = $request->session()->only(['username', 'email']);
$data = $request->session()->except(['username', 'email']);
Determinando si un Elemento Existe en la Sesión
Para determinar si un elemento está presente en la sesión, puedes usar el método has
. El método has
devuelve true
si el elemento está presente y no es null
:
if ($request->session()->has('users')) {
// ...
}
Para determinar si un elemento está presente en la sesión, incluso si su valor es null
, puedes usar el método exists
:
if ($request->session()->exists('users')) {
// ...
}
Determinando si un Elemento No Está Presente en la Sesión
Para determinar si un elemento no está presente en la sesión, puedes usar el método missing
. El método missing
devuelve true
si el elemento no está presente:
if ($request->session()->missing('users')) {
// ...
}
Almacenando Datos
Para almacenar datos en la sesión, típicamente usarás el método put
de la instancia de solicitud o el helper global session
:
// A través de una instancia de solicitud...
$request->session()->put('key', 'value');
// A través del helper global "session"...
session(['key' => 'value']);
Agregando Valores a Arrays en la Sesión
El método push
puede usarse para agregar un nuevo valor a un valor de sesión que es un array. Por ejemplo, si la clave user.teams
contiene un array de nombres de equipos, puedes agregar un nuevo valor al array de la siguiente manera:
$request->session()->push('user.teams', 'developers');
Recuperando y Eliminando un Elemento
El método pull
recuperará y eliminará un elemento de la sesión en una sola declaración:
$value = $request->session()->pull('key', 'default');
Incrementando y Decrementando Valores de Sesión
Si los datos de tu sesión contienen un entero que deseas incrementar o decrementar, puedes usar los métodos increment
y decrement
:
$request->session()->increment('count');
$request->session()->increment('count', $incrementBy = 2);
$request->session()->decrement('count');
$request->session()->decrement('count', $decrementBy = 2);
Datos Flash
A veces puede que desees almacenar elementos en la sesión para la siguiente solicitud. Puedes hacerlo usando el método flash
. Los datos almacenados en la sesión usando este método estarán disponibles inmediatamente y durante la solicitud HTTP subsecuente. Después de la solicitud HTTP subsecuente, los datos flash se eliminarán. Los datos flash son principalmente útiles para mensajes de estado de corta duración:
$request->session()->flash('status', '¡La tarea fue exitosa!');
Si necesitas persistir tus datos flash durante varias solicitudes, puedes usar el método reflash
, que mantendrá todos los datos flash para una solicitud adicional. Si solo necesitas mantener datos flash específicos, puedes usar el método keep
:
$request->session()->reflash();
$request->session()->keep(['username', 'email']);
Para persistir tus datos flash solo para la solicitud actual, puedes usar el método now
:
$request->session()->now('status', '¡La tarea fue exitosa!');
Eliminando Datos
El método forget
eliminará un dato de la sesión. Si deseas eliminar todos los datos de la sesión, puedes usar el método flush
:
// Olvidar una sola clave...
$request->session()->forget('name');
// Olvidar múltiples claves...
$request->session()->forget(['name', 'status']);
$request->session()->flush();
Regenerando el ID de Sesión
Regenerar el ID de sesión a menudo se hace para evitar que usuarios malintencionados exploten un ataque de fijación de sesión en tu aplicación.
Laravel regenera automáticamente el ID de sesión durante la autenticación si estás usando uno de los kits de inicio de aplicaciones de Laravel o Laravel Fortify; sin embargo, si necesitas regenerar manualmente el ID de sesión, puedes usar el método regenerate
:
$request->session()->regenerate();
Si necesitas regenerar el ID de sesión y eliminar todos los datos de la sesión en una sola declaración, puedes usar el método invalidate
:
$request->session()->invalidate();
Bloqueo de Sesión
Para utilizar el bloqueo de sesión, tu aplicación debe estar usando un driver de caché que soporte bloqueos atómicos. Actualmente, esos drivers de caché incluyen memcached, dynamodb, redis, database, file y array. Además, no puedes usar el driver de sesión de cookies.
Por defecto, Laravel permite que las solicitudes que usan la misma sesión se ejecuten concurrentemente. Entonces, por ejemplo, si usas una biblioteca HTTP de JavaScript para hacer dos solicitudes HTTP a tu aplicación, ambas se ejecutarán al mismo tiempo. Para muchas aplicaciones, esto no es un problema; sin embargo, la pérdida de datos de sesión puede ocurrir en un pequeño subconjunto de aplicaciones que hacen solicitudes concurrentes a dos endpoints diferentes de la aplicación que ambos escriben datos en la sesión.
Para mitigar esto, Laravel proporciona funcionalidad que te permite limitar las solicitudes concurrentes para una sesión dada. Para comenzar, simplemente puedes encadenar el método block
a tu definición de ruta. En este ejemplo, una solicitud entrante al endpoint /profile
adquiriría un bloqueo de sesión. Mientras se mantiene este bloqueo, cualquier solicitud entrante a los endpoints /profile
o /order
que compartan el mismo ID de sesión esperarán a que la primera solicitud termine de ejecutarse antes de continuar su ejecución:
Route::post('/profile', function () {
// ...
})->block($lockSeconds = 10, $waitSeconds = 10);
Route::post('/order', function () {
// ...
})->block($lockSeconds = 10, $waitSeconds = 10);
El método block
acepta dos argumentos opcionales. El primer argumento aceptado por el método block
es el número máximo de segundos que el bloqueo de sesión debe mantenerse antes de ser liberado. Por supuesto, si la solicitud termina de ejecutarse antes de este tiempo, el bloqueo se liberará antes.
El segundo argumento aceptado por el método block
es el número de segundos que una solicitud debe esperar mientras intenta obtener un bloqueo de sesión. Se lanzará una excepción Illuminate\Contracts\Cache\LockTimeoutException
si la solicitud no puede obtener un bloqueo de sesión dentro del número dado de segundos.
Si no se pasan ninguno de estos argumentos, el bloqueo se obtendrá por un máximo de 10 segundos y las solicitudes esperarán un máximo de 10 segundos mientras intentan obtener un bloqueo:
Route::post('/profile', function () {
// ...
})->block();
Agregando Drivers de Sesión Personalizados
Implementando el Driver
Si ninguno de los drivers de sesión existentes se ajusta a las necesidades de tu aplicación, Laravel hace posible escribir tu propio manejador de sesión. Tu driver de sesión personalizado debe implementar la interfaz SessionHandlerInterface
incorporada en PHP. Esta interfaz contiene solo unos pocos métodos simples. Una implementación de MongoDB con métodos vacíos se vería como la siguiente:
<?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 no incluye un directorio para contener tus extensiones. Eres libre de colocarlas donde quieras. En este ejemplo, hemos creado un directorio Extensions
para alojar el MongoSessionHandler
.
Dado que el propósito de estos métodos no es fácilmente comprensible, cubramos rápidamente lo que hace cada uno de los métodos:
- El método
open
se usaría típicamente en sistemas de almacenamiento de sesiones basados en archivos. Dado que Laravel incluye un driver de sesión de archivos, rara vez necesitarás poner algo en este método. Puedes simplemente dejar este método vacío. - El método
close
, al igual que el métodoopen
, también puede ser ignorado generalmente. Para la mayoría de los drivers, no es necesario. - El método
read
debe devolver la versión en cadena de los datos de la sesión asociados con el$sessionId
dado. No hay necesidad de hacer ninguna serialización u otra codificación al recuperar o almacenar datos de sesión en tu driver, ya que Laravel realizará la serialización por ti. - El método
write
debe escribir la cadena de datos dada asociada con el$sessionId
en algún sistema de almacenamiento persistente, como MongoDB u otro sistema de almacenamiento de tu elección. Nuevamente, no debes realizar ninguna serialización: Laravel ya se habrá encargado de eso por ti. - El método
destroy
debe eliminar los datos asociados con el$sessionId
del almacenamiento persistente. - El método
gc
debe destruir todos los datos de sesión que sean más antiguos que el$lifetime
dado, que es una marca de tiempo UNIX. Para sistemas autodestructivos como Memcached y Redis, este método puede dejarse vacío.
Registrando el Driver
Una vez que tu driver ha sido implementado, estás listo para registrarlo con Laravel. Para agregar drivers adicionales al backend de sesión de Laravel, puedes usar el método extend
proporcionado por la fachada Session
. Debes llamar al método extend
desde el método boot
de un proveedor de servicios. Puedes hacer esto desde el App\Providers\AppServiceProvider
existente o crear un proveedor completamente nuevo:
<?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
{
/**
* Registrar cualquier servicio de la aplicación.
*/
public function register(): void
{
// ...
}
/**
* Inicializar cualquier servicio de la aplicación.
*/
public function boot(): void
{
Session::extend('mongo', function (Application $app) {
// Devolver una implementación de SessionHandlerInterface...
return new MongoSessionHandler;
});
}
}
Una vez que el driver de sesión ha sido registrado, puedes especificar el driver mongo
como el driver de sesión de tu aplicación usando la variable de entorno SESSION_DRIVER
o dentro del archivo de configuración config/session.php
de la aplicación.