Blog EspecializaTi
Carlos Ferreira Por: Carlos Ferreira Comentar

Login Único no Laravel

Login Único no Laravel

Em algum momento quando desenvolver sistemas em Laravel, certamente vai precisar restringir o login do usuário, permitindo que apenas um usuário acesse usando os mesmos dados.

Existem inúmeras formas de implementar isso, e neste tutorial vou procurar implementar usando uma das técnicas mais simples possível, que é criar um hash para validação do login do usuário e usar uma session (sessão) para validar essa hash.

Como vai funcionar a lógica neste caso?

Vamos criar uma coluna na tabela do usuário (users) chamada por exemplo “token_access“, o valor default dessa coluna será null.

Quando o usuário acessar o sistema (fazer login) vamos criar um hash único e inserir nessa tabela este hash, mas, não só isso, vamos também armazenar este mesmo hash em uma sessão.

Após inserir o hash na coluna token_access e armazenar na sessão, podemos criar um Middleware (filtro) que vai comparar se o valor da coluna token_access é igual ao hash da sessão.

Se o valor da coluna token_access é diferente ao hash da sessão siginfica que o usuário fez um novo login e gerou um novo hash. Neste caso podemos deslogar o usuário, caso os valores não se coinsidam.

 
 

Pré-requisitos:

Antes de continuar é importante que você já domine o Laravel, saiba usar o sistema de autenticação do Laravel. Alguns outros pré-requisitos são (leia os tutoriais):

Se já domina estes assuntos, vamos colocar criar nossos códigos! =D

Primeiro passo, criar a coluna token_access na tabela users, acesse a migration de usuário e adicione essa nova opção:

$table->string('token_access')->nullable();

Após fazer isso rode o comando para atualizar as tabelas:

php artisan migrate:refresh --seed

Uma vez que a estrutura da nossa tabela de usuários (users) está pronta, podemos ir para o próximo passo, que é capturar o evento de login do usuário. Antes de continuar é importante saber como trabalhar com estes eventos de login (leia este tutorial).

O próximo passo é criar o Listener UserEventSubscriber, você pode criar manual essa classe em app/Listeners/UserEventSubscriber.php ou pode usar o comando do artisan para gerar a classe:

php artisan make:listener UserEventSubscriber

No arquivo de registro de Eventos de vamos registrar o evento default (padrão) para login/logout/register do usuário, para isso acesse o arquivo app/Providers/EventServiceProvider.php e crie este atributo para registrar o listener UserEventSubscriber, que é disparado após uma ação de authentication do Laravel:

/**
 * UserEventSubscriber
 *
 * @var array
 */
protected $subscribe = [
    'App\Listeners\UserEventSubscriber',
];

Se criar essa classe pelo artisan pode deletar os métodos __construct() e handle(), porque não vamos usar eles.

Crie um método subscribe() que recebe como parâmentro $events, que são os eventos interceptados para authentication do usuário:

/**
 * Register the listeners for the subscriber.
 *
 * @param  Illuminate\Events\Dispatcher  $events
 */
public function subscribe($events)
{

}

Com o parâmetro $events podemos interceptar os eventos de authentication e direcionar para métodos e classes específicas. No caso, vamos pegar o evento de login, e direcionar a ação de pós login para um método desta mesma classe chamado onUserLogin(). No método subscribe() pode direcionar essa ação de pós login:

$events->listen(
            'Illuminate\Auth\Events\Login',
            'App\Listeners\[email protected]'
        );

O próximo passo é criar o método onUserLogin() na classe UserEventSubscriber, para tomar as ações pós-login:

public function onUserLogin($event)
{
    
}

Já dentro do métdo onUserLogin() é possível pegar os dados do usuário logado através de auth()->user() e fazer o que for necessário, neste caso, registrar o token de acesso.

Vamos criar o token de acesso através do timestamps atual e criptografar com o helper bcrypt() do Laravel:

$tokenAccess = bcrypt(date('YmdHms'));

Agora pode inserir este token na coluna token_access da tabela users no registro do usuário logado:

$user = auth()->user();
$user->token_access = $tokenAccess;
$user->save();

E por último podemos registrar a sessão com este mesmo valor de $tokenAccess. Porque lembre que vamos usar o valor da coluna token_access para comparar com o valor da sessão.

session()->put('access_token', $tokenAccess);

Resultado final da classe app/Listeners/UserEventSubscriber.php:

namespace App\Listeners;

class UserEventSubscriber
{
    /**
     * Handle user login events.
     */
    public function onUserLogin($event)
    {
        $tokenAccess = bcrypt(date('YmdHms'));

        $user = auth()->user();
        $user->token_access = $tokenAccess;
        $user->save();

        session()->put('access_token', $tokenAccess);
    }

    /**
     * Register the listeners for the subscriber.
     *
     * @param  Illuminate\Events\Dispatcher  $events
     */
    public function subscribe($events)
    {
        $events->listen(
            'Illuminate\Auth\Events\Login',
            'App\Listeners\[email protected]'
        );
 
        /*
        $events->listen(
            'Illuminate\Auth\Events\Logout',
            'App\Listeners\[email protected]'
        );
        */
    }
}

 
 

Middleware de verificação:

Agora o próximo passo é criar um Middleware que vai validar o valor da coluna access_token com o valor da sessão que criamos “access_token”.

Para criar o Middleware, rode este comando:

php artisan make:middleware CheckUserUniqueAuth

No método handle() do nosso middleware app/Http/Middleware/CheckUserUniqueAuth.php vamos verificar se os valores são compatíveis, se não forem podemos deslogar o usuário… veja como fica:

/**
 * Handle an incoming request.
 *
 * @param  \Illuminate\Http\Request  $request
 * @param  \Closure  $next
 * @return mixed
 */
public function handle($request, Closure $next)
{
    /* Verifica se o valor da coluna/sessão "token_access" NÃO é compatível com o valor da sessão que criamos quando o usuário fez login
    */
    if (auth()->user()->token_access != session()->get('access_token')) {
        // Faz o logout do usuário
        \Auth::logout();

        // Redireciona o usuário para a página de login, com session flash "message"
        return redirect()
                    ->route('login')
                    ->with('message', 'A sessão deste usuário está ativa em outro local!');
    }

    // Permite o acesso, continua a requisição
    return $next($request);
}

O próximo passo é registrar este Middleware, no arquivo app/Http/Kernel.php no atributo $routeMiddleware adicione essa nova opção no array:

'auth.unique.user' => \App\Http\Middleware\CheckUserUniqueAuth::class,

PS. Não esqueça de exibir a mensagem da session flash “message” na view de logon resources/views/auth/login.blade.php:

@if (session()->has('message'))
    <div class="alert alert-warning alert-dismissible">
        {{ session('message') }}
    </div>
@endif

Por último, basta simplesmente usar este novo Middleware que criamos, pode ser nas rotas, ou diretamente no controller. Neste caso podemos fazer algo assim:

$this->group(['middleware' => ['auth', 'auth.unique.user']], function(){
    // Rotas autenticadas
});

Se quiser testar na estruta defalt que autenticação que o Laravel traz, usando a rota “home”:

Route::get('/home', '[email protected]')
        ->name('home')
        ->middleware('auth.unique.user');

Agora simplesmente faça o teste, acesse o projeto com um usuário em um Browser, e use este mesmo dado de acesso e logue em outro Browser. Notará que ao logar em outro navegador o usuário que logou primeiro será deslogado.

Código fonte: https://github.com/carlosfgti/laravel-login-unico

 

Obrigado pela leitura! 🙂

Abraços []’s

 
 

Carlos Ferreira

Sobre o Autor:

Carlos Ferreira

Carlos Ferreira é Analista de Sistemas Experiente, Empreendedor, Fundador da empresa EspecializaTi. Certificações: Comptia Linux +, LPI, Novell Certification.

Todos os direitos reservados © 2018 - EspecializaTi. É proibida a reprodução total ou parcial deste conteúdo.