Sessões
Introdução
Uma vez que as aplicações baseadas em HTTP não conservam o estado das informações do utilizador, as sessões permitem armazenar informações sobre o utilizador entre requisições de forma a poderem ser acessadas em pedidos subsequentes.
O Laravel é entregue com uma variedade de sessão backends que são acessados por meio de uma API expressiva e unificada. A inclusão de suporte para backends populares como Memcached, Redis e bancos de dados está incluída.
Configuração
O arquivo de configuração da sessão do seu aplicativo está armazenado em config/session.php
. Reverifique as opções disponíveis neste arquivo. Por padrão, o Laravel é configurado para usar o driver de sessões do "banco de dados".
A opção de configuração do driver
da sessão define onde os dados das sessões serão armazenados para cada solicitação. O Laravel inclui vários "drivers":
file
: as sessões são armazenadas emstorage/framework/sessões
.cookie
– as sessões são armazenadas em cookies criptografados e seguros.database
- as sessões são armazenadas num banco de dados relacional.memcached
ouredis
: as sessões são armazenadas em um desses servidores rápidos baseados em cache.dynamodb
- as sessões são armazenadas no DynamoDB da AWS.array
- As sessões são armazenadas num array PHP e não serão persistidas.
ATENÇÃO
O driver de array é usado principalmente durante testes e evita que os dados armazenados na sessão sejam persistidos.
Pré-requisitos de um driver
Banco de dados
Para usar o driver de sessão database
, é necessário que você tenha uma tabela de banco de dados para conter os dados da sessão. Normalmente, isso está incluído na migração de banco de dados padrão do Laravel chamada 0001_01_01_000000_create_users_table.php
. No entanto, se você não tiver uma tabela sessions
por algum motivo, poderá usar o comando Artisan make:session-table
para gerar esta migração:
php artisan make:session-table
php artisan migrate
Redis
Antes de usar sessões do Redis com o Laravel, você precisará instalar a extensão PHP PhpRedis por meio da PECL ou instalar o pacote predis/predis
(~1.0) por meio do Composer. Para obter mais informações sobre como configurar o Redis, consulte a Documentação Redis do Laravel.
NOTA
A variável de ambiente SESSION_CONNECTION
, ou a opção connection
no arquivo de configuração session.php
, pode ser usada para especificar qual conexão Redis é usada para armazenamento de sessão.
Interagir com a sessão
Recuperando dados
Existem duas maneiras de trabalhar com dados de sessão em Laravel: o recurso auxiliar global session
e por meio de uma instância do tipo Request
. Primeiro, vejamos como acessar a sessão por meio de uma instância do tipo Request
, que pode ser indicada na closure de rotas ou métodos do controlador. Lembre-se de que as dependências dos métodos do controlador são injetadas automaticamente através do conjunto de serviços Laravel:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\View\View;
class UserController extends Controller
{
/**
* Mostre o perfil do usuário fornecido.
*/
public function show(Request $request, string $id): View
{
$value = $request->session()->get('key');
// ...
$user = $this->users->find($id);
return view('user.profile', ['user' => $user]);
}
}
Quando recuperar um item da sessão, você poderá também passar um valor padrão como o segundo argumento ao método get
. Este valor será devolvido se a chave especificada não existir na sessão. Se for passado um bloco de código como o valor padrão para o método get
e a chave solicitada não existir, é executado o bloco de código e retorna-se o resultado:
$value = $request->session()->get('key', 'default');
$value = $request->session()->get('key', function () {
return 'default';
});
O assistente de sessão global
Você também pode usar as funções PHP globais session
para recuperar e armazenar dados na sessão. Quando o auxiliar session
é chamado com um único argumento de string, ele retorna o valor da chave da sessão. Se o auxiliar for chamado com uma lista de pares de chaves/valores, esses valores serão armazenados na sessão:
Route::get('/home', function () {
// Recuperar um dado da sessão...
$value = session('key');
// Especificando um valor padrão...
$value = session('key', 'default');
// Armazene um dado na sessão...
session(['key' => 'value']);
});
NOTA
Há pouca diferença prática entre usar a sessão por meio de uma instância de solicitação HTTP e usar o auxiliar global session
. Ambos os métodos são testáveis através do método assertSessionHas
que está disponível em todos os seus casos de teste.
Recuperar todos os dados da sessão
Se você quiser recuperar todos os dados da sessão, poderá usar o método all
:
$data = $request->session()->all();
Recuperar uma parte dos dados de sessão
Os métodos only
e except
podem ser utilizados para recuperar um subconjunto dos dados da sessão:
$data = $request->session()->only(['username', 'email']);
$data = $request->session()->except(['username', 'email']);
Determinar se um item existe na sessão
Para determinar se um objeto está presente na sessão é possível usar o método has
. O método has
retorna true
quando o objeto está presente e não é null
:
if ($request->session()->has('users')) {
// ...
}
Para determinar se um item está presente na sessão, mesmo que seu valor seja null
, você pode usar o método exists
:
if ($request->session()->exists('users')) {
// ...
}
Para determinar se um elemento não está presente na sessão, você pode usar o método missing
. O método missing
retorna verdadeiro
se o elemento estiver faltando:
if ($request->session()->missing('users')) {
// ...
}
Armazenar dados
Para armazenar dados na sessão, normalmente você utilizará o método put
da instância de solicitação ou o recurso auxiliar global session
:
// Por meio de uma instância de solicitação...
$request->session()->put('key', 'value');
// Através do auxiliar global de "sessão"...
session(['key' => 'value']);
Adicionando valores de array na sessão
É possível utilizar o método push
para adicionar um novo valor em uma variável de sessão que seja um tipo de matriz. Por exemplo, se a chave user.teams
fornecer uma lista de nomes de equipes, pode ser efetuada a sua expansão desta forma:
$request->session()->push('user.teams', 'developers');
Recuperar e excluir um item
O método pull
recupera e exclui um elemento da sessão em uma única instrução:
$value = $request->session()->pull('key', 'default');
Aumentar e diminuir os valores da sessão
Se os dados da sessão incluírem um número inteiro que você deseja incrementar ou decrementar, você poderá usar os métodos increment
e decrement
:
$request->session()->increment('count');
$request->session()->increment('count', $incrementBy = 2);
$request->session()->decrement('count');
$request->session()->decrement('count', $decrementBy = 2);
Dados do Flash
Às vezes você pode desejar armazenar itens na sessão para a próxima requisição . Você pode fazer isso usando o método flash
. Dados armazenados na sessão com este método estarão disponíveis imediatamente e durante o pedido subsequente. Após o pedido HTTP subsequente, os dados "flash" serão excluídos. Os dados "flash" são úteis principalmente para mensagens de status de curto prazo:
$request->session()->flash('status', 'Task was successful!');
Se você precisar persistir seus dados Flash para vários pedidos, você pode usar o método reflash
, que manterá todos os dados Flash por mais uma requisição. Se você só precisa manter um tipo específico de dado Flash, poderá usar o método keep
:
$request->session()->reflash();
$request->session()->keep(['username', 'email']);
Para persistir dados de flash somente para o pedido atual, você pode usar o método now
:
$request->session()->now('status', 'Task was successful!');
Excluir dados
O método forget
removerá um dado da sessão. Para remover todos os dados de uma sessão, você poderá usar o método flush
:
// Esqueça uma única chave...
$request->session()->forget('name');
// Esqueça várias chaves...
$request->session()->forget(['name', 'status']);
$request->session()->flush();
Regenerando o identificador de sessão
A regeneração do identificador da sessão é feita frequentemente para evitar que usuários maliciosos explorem um ataque de fixação de sessão em seu aplicativo.
O Laravel regenera automaticamente o identificador de sessão durante a autenticação se você estiver usando um dos Kits iniciais da aplicação Laravel ou Laravel Fortify. No entanto, se for preciso regenerar manualmente o identificador de sessão, é possível usar o método regenerate
:
$request->session()->regenerate();
Se você precisar gerar um novo identificador de sessão e remover todos os dados da sessão em uma única declaração, poderá usar o método invalidate
:
$request->session()->invalidate();
Bloqueio de sessão
ATENÇÃO
Para utilizar o bloqueio de sessão, seu aplicativo deve usar um driver de cache que suporte bloqueios atômicos. Atualmente, esses drivers de cache incluem os drivers memcached
, dynamodb
, redis
, database
, file
e array
. Além disso, você não pode usar o driver de sessão cookie
.
Por padrão, o Laravel permite que os pedidos usem a mesma sessão para serem executados simultaneamente. Então, por exemplo, se você usar uma biblioteca de HTTP em JavaScript para fazer dois pedidos ao seu aplicativo, ambos serão executados ao mesmo tempo. Para muitas aplicações, isso não é um problema; entretanto, a perda de dados da sessão pode ocorrer num pequeno subconjunto de aplicações que fizerem requisições concorrentes para dois pontos finais diferentes do aplicativo, sendo ambos responsáveis por gravar os dados na sessão.
Para evitar esse problema, o Laravel fornece uma funcionalidade que permite limitar solicitações concorrentes para uma sessão específica. Para começar, você pode simplesmente adicionar a definição da rota ao método block
. Nesse exemplo, uma solicitação entrando no endpoint "/profile" irá bloquear a sessão. Enquanto esse bloqueio estiver sendo mantido, todas as solicitações entrantes nos endpoints "/profile" ou "/order", que compartilham o mesmo identificador de sessão, esperarão que a primeira solicitação termine sua execução antes de continuarem:
Route::post('/profile', function () {
// ...
})->block($lockSeconds = 10, $waitSeconds = 10)
Route::post('/order', function () {
// ...
})->block($lockSeconds = 10, $waitSeconds = 10)
O método block
aceita dois argumentos opcionais. O primeiro argumento aceito pelo método block
é o número máximo de segundos durante os quais a sessão deve permanecer bloqueada antes de ser liberada. Logo, se o pedido for executado e terminar antes desse tempo, o bloqueio será liberado mais cedo.
O segundo argumento aceito pelo método block
é o número de segundos que o pedido deve esperar para tentar obter um bloqueio de sessão. Será lançado uma exceção Illuminate\Contracts\Cache\LockTimeoutException
se o pedido não conseguir obter um bloqueio de sessão dentro do período especificado em segundos.
Se nenhum destes argumentos for passado, o bloqueio será feito por um período máximo de 10 segundos. As requisições irão aguardar por um período máximo de 10 segundos enquanto tentam obter um bloqueio:
Route::post('/profile', function () {
// ...
})->block()
Adicionando drivers de sessão personalizados
Implementação do driver
Se nenhum dos drivers de sessão existentes atenderem às suas necessidades, o Laravel permite que você crie seu próprio driver de sessão. O driver de sessão personalizado deve implementar a SessionHandlerInterface
incorporada ao PHP. Essa interface contém apenas alguns métodos simples. Uma implementação de MongoDB simples, chamada de "stub", é mostrada a seguir:
<?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) {}
}
NOTA
O Laravel não vem com um diretório para conter suas extensões. Você é livre para colocá-los onde quiser. Neste exemplo, criamos um diretório Extensions
para hospedar o MongoSessionHandler
.
Uma vez que a finalidade destes métodos não é compreendida facilmente, vamos ver rapidamente o que cada um deles faz:
- O método
open
é normalmente utilizado em sistemas de armazenamento de sessões baseados em arquivos. Uma vez que o Laravel inclui um driver de sessãofile
, é muito raro ter necessidade de preencher este método. Pode se deixar vazio. - O método
close
, assim como o métodoopen
, geralmente pode ser desconsiderado. Para a maioria dos drivers não é necessário. - O método
read
deve retornar a versão em string dos dados de sessão associada ao$sessionId
informado. Não é necessário realizar nenhuma serialização ou outra codificação ao recuperar ou armazenar os dados da sessão em seu driver, pois o Laravel irá realizar a serialização por você. - O método
write
deverá escrever a string$data
associado ao$sessionId
para um sistema persistente de armazenamento, como MongoDB ou outro sistema de seu interesse. Novamente, não deve ser efetuada nenhuma serialização - Laravel já lidou com isso para você. - O método
destroy
deve remover os dados associados ao$sessionId
do armazenamento persistente. - O método
gc
deve destruir todos os dados de sessão mais antigos do que o valor da variável$lifetime
, que é um timestamp UNIX. Para sistemas com auto-expiração, como Memcached e Redis, este método pode ficar vazio.
Registrar o driver
Depois que seu driver for implementado, você estará pronto para registrá-lo com o Laravel. Para adicionar drivers adicionais ao back-end de sessão do Laravel, é possível usar a método extend
disponibilizado pela facade Session
. Você deve chamar a método extend
no método boot
de um service provider. Isso pode ser feito tanto no existente App\Providers\AppServiceProvider
, quanto criando um novo provedor:
<?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
{
/**
* Registre quaisquer serviços do aplicativo.
*/
public function register(): void
{
// ...
}
/**
* Inicialize qualquer serviço do aplicativo.
*/
public function boot(): void
{
Session::extend('mongo', function (Application $app) {
// Retornar uma implementação de SessionHandlerInterface...
return new MongoSessionHandler;
});
}
}
Depois que o driver de sessão tiver sido registrado, você pode especificar o driver mongo
como seu driver de sessão de aplicativo usando a variável de ambiente SESSION_DRIVER
ou na configuração do arquivo de configuração config/session.php
da aplicação.