Control de usuarios

Laravel incluye una serie de métodos y clases que harán que la implementación del control de usuarios sea muy rápida y sencilla. De hecho, casi todo el trabajo ya está hecho, solo tendremos que indicar donde queremos utilizarlo y algunos pequeños detalles de configuración.

Por defecto, al crear un nuevo proyecto de Laravel, ya se incluye todo lo necesario:

  • La configuración predeterminada en config/auth.php.

  • La migración para la base de datos de la tabla de usuarios con todos los campos necesarios.

  • El modelo de datos de usuario (User.php) dentro de la carpeta app con toda la implementación necesaria.

  • Los controladores para gestionar todas las acciones relacionadas con el control de usuarios (dentro de App\Http\Controllers\Auth).

Además de esto tendremos que ejecutar el siguiente comando para generar las rutas y vistas necesarias para realizar el login, registro y para recuperar la contraseña:

php artisan make:auth

En los siguientes apartados vamos a ver en detalle cada uno de estos puntos, desde la configuración hasta los módulos, rutas y vistas por los que está compuesto. En las últimas secciones revisaremos también cómo utilizar este sistema para proteger nuestro sitio web.

Configuración inicial

La configuración del sistema de autenticación se puede encontrar en el fichero config/auth.php, el cual contiene varias opciones (bien documentadas) que nos permitirán, por ejemplo: cambiar el sistema de autenticación (que por defecto es a través de Eloquent), cambiar el modelo de datos usado para los usuarios (por defecto será User) y cambiar la tabla de usuarios (que por defecto será users). Si vamos a utilizar estos valores no será necesario que realicemos ningún cambio.

La migración de la tabla de usuarios (llamada users) también está incluida (ver carpeta database/migrations). Por defecto incluye todos los campos necesarios (ver el código siguiente), pero si necesitamos alguno más lo podemos añadir para guardar por ejemplo la dirección o el teléfono del usuario. A continuación se incluye el código de la función up de la migración:

Schema::create('users', function (Blueprint $table) {
    $table->increments('id');
    $table->string('name');
    $table->string('email')->unique();
    $table->string('password');
    $table->rememberToken();
    $table->timestamps();
});

Como se puede ver el nombre de la tabla es users, con un índice id autoincremental, y los campos de name, email, password, donde el campo email se establece como único para que no se puedan almacenar emails repetidos. Además se añaden los timestamps que usa Eloquent para almacenar automáticamente la fecha de registro y actualización, y el campo remember_token para recordar la sesión del usuario.

En la carpeta app se encuentra el modelo de datos (llamado User.php) para trabajar con los usuarios. Esta clase ya incluye toda la implementación necesaria y por defecto no tendremos que modificar nada. Pero si queremos podemos modificar esta clase para añadirle más métodos o relaciones con otras tablas, etc.

Laravel también incluye varios controladores (LoginController, RegisterController, ResetPasswordController y ForgotPasswordController) para la autenticación de usuarios, los cuales los puedes encontrar en el espacio de nombres App\Http\Controllers\Auth (y en la misma carpeta). LoginController y RegisterController incluyen métodos para ayudarnos en el proceso de autenticación, registro y cierre de sesión; mientras que ResetPasswordController y ForgotPasswordController contienen la lógica para ayudarnos en el proceso de restaurar una contraseña. Para la mayoría de aplicaciones con estos métodos será suficiente y no tendremos que añadir nada más.

Lo único que falta por añadir y configurar correctamente para que todo funcione son las rutas y las vistas, que como hemos mencionado en la introducción se generan mediante el siguiente comando:

php artisan make:auth

En las siguientes secciones vamos a ver las rutas y vistas que se generan y cómo se tienen que utilizar.

Rutas

Por defecto Laravel no incluye las rutas para el control de usuarios, para generarlas tenemos que ejecutar el comando php artisan make:auth. Este comando nos añadirá las siguientes líneas al fichero routes/web.php:

Auth::routes();
Route::get('/home', 'HomeController@index');

La primera línea (Auth::routes) es la que añade todas las rutas para el login, logout, registro y para recuperar la contraseña. La segunda entrada añade la ruta home que apunta al controlador HomeController (el cual se habrá creado también), esta ruta es la que se utiliza para redirigir a los usuarios cuando realicen el login correctamente. En la siguiente tabla se puede ver un resumen de todas las rutas añadidas:

Como se puede ver estas rutas ya están enlazadas con los controladores y métodos que incorpora el propio Laravel. Al acceder a la ruta login por GET se llamará a LoginController@showLoginForm el cual se encargará de mostrar el formulario de login, y lo mismo para la ruta register tipo GET para el registro. Las rutas login y register por POST se encargarán de procesar los datos enviados por los formularios. Al acceder a la ruta logout por POST se cerrará la sesión. Además hay cuatro rutas que se utilizan para recuperar la contraseña del usuario: al acceder a password/reset por GET se mostrará el formulario para que escribamos nuestra dirección de correo y enviarnos el link para restablecer la contraseña (lo cual lo gestionará la petición a password/email mediante POST). Al pulsar sobre el link enviado al correo se accederá a password/reset/{token} por GET con el token generado, esto nos mostrará el formulario para que establezcamos una nueva contraseña y finalmente lo enviemos a password/reset mediante POST.

Las direcciones URLs generadas son fijas, si queremos modificarlas lo más sencillo es que comentemos la línea Auth::routes() que se ha añadido al fichero de rutas y que las escribamos nosotros manualmente, copiando lo mismo que generaría Auth::routes() pero cambiando únicamente las URLs.

Vistas

Al ejecutar el comando php artisan make:auth también se generarán todas las vistas necesarias para realizar el login, registro y para recuperar la contraseña. Todas estas vistas las podremos encontrar en la carpeta resources/views/auth con los nombres login.blade.php para el formulario de login, register.blade.php para el formulario de registro, passwords/email.blade.php para el formulario de recuperar contraseña y passwords/reset.blade.php para el formulario de restaurar la contraseña. Estos nombres y rutas son obligatorios ya que los controladores que incluye Laravel accederán a ellos, por lo que no deberemos cambiarlos.

Si abrimos cualquiera de ellos veremos como utiliza el sistema de plantillas de Blade, en todos ellos hereda del layout layouts/app.blade.php mediante la directiva @extends('layouts.app') y rellena el contenido de su zona central content. Esta plantilla de layout establece la estructura básica, carga la hoja de estilo y Javascript de Bootstrap, y añade el menú principal con botones para acceder al login y registro. Cada uno de los formularios está enlazado con la ruta correspondiente, y además incluye código para mostrar los errores generados y volver a rellenar los campos tras un error con el texto que haya escrito el usuario.

A modo de ejemplo se incluye el código para la vista del formulario de login:

@extends('layouts.app')

@section('content')
<div class="container">
    <div class="row">
        <div class="col-md-8 col-md-offset-2">
            <div class="panel panel-default">
                <div class="panel-heading">Login</div>
                <div class="panel-body">
                    <form class="form-horizontal" role="form" method="POST" action="{{ url('/login') }}">
                        {{ csrf_field() }}
                        <div class="form-group{{ $errors->has('email') ? ' has-error' : '' }}">
                            <label for="email" class="col-md-4 control-label">E-Mail Address</label>
                            <div class="col-md-6">
                                <input id="email" type="email" class="form-control" name="email" value="{{ old('email') }}" required autofocus>
                                @if ($errors->has('email'))
                                    <span class="help-block">
                                        <strong>{{ $errors->first('email') }}</strong>
                                    </span>
                                @endif
                            </div>
                        </div>
                        <div class="form-group{{ $errors->has('password') ? ' has-error' : '' }}">
                            <label for="password" class="col-md-4 control-label">Password</label>
                            <div class="col-md-6">
                                <input id="password" type="password" class="form-control" name="password" required>
                                @if ($errors->has('password'))
                                    <span class="help-block">
                                        <strong>{{ $errors->first('password') }}</strong>
                                    </span>
                                @endif
                            </div>
                        </div>
                        <div class="form-group">
                            <div class="col-md-6 col-md-offset-4">
                                <div class="checkbox">
                                    <label>
                                        <input type="checkbox" name="remember"> Remember Me
                                    </label>
                                </div>
                            </div>
                        </div>
                        <div class="form-group">
                            <div class="col-md-8 col-md-offset-4">
                                <button type="submit" class="btn btn-primary">
                                    Login
                                </button>
                                <a class="btn btn-link" href="{{ url('/password/reset') }}">
                                    Forgot Your Password?
                                </a>
                            </div>
                        </div>
                    </form>
                </div>
            </div>
        </div>
    </div>
</div>
@endsection

Si lo deseamos podemos modificar el contenido y diseño de cualquier vista, así como del layout, lo único que tenemos que mantener igual es la URL a la que se envía el formulario y los nombres de cada uno de los inputs del formulario.

Autenticación de un usuario

Una vez configurado todo el sistema, añadidas las rutas y las vistas para realizar el control de usuarios ya podemos utilizarlo. Si accedemos a la ruta login nos aparecerá la vista con el formulario de login, solicitando nuestro email y contraseña para acceder. El campo tipo checkbox llamado "remember" nos permitirá indicar si deseamos que la sesión permanezca abierta hasta que se cierre manualmente. Es decir, aunque se cierre el navegador y pasen varios días el usuario seguiría estando autorizado.

Si los datos introducidos son correctos se creará la sesión del usuario y se le redirigirá a la ruta "/home". Si queremos cambiar esta ruta tenemos que definir la propiedad redirectTo en el controlador LoginController, por ejemplo:

protected $redirectTo = '/';

Además podemos definir esta propiedad en RegisterController y ResetPasswordController para cambiar la URL de redirección después del registro y después de restablecer la contraseña, respectivamente.

Registro de un usuario

Si accedemos a la ruta register nos aparecerá la vista con el formulario de registro, solicitándonos los campos nombre, email y contraseña. Al pulsar el botón de envío del formulario se llamará a la ruta register por POST y se almacenará el nuevo usuario en la base de datos.

Si no hemos añadido ningún campo más en la migración no tendremos que configurar nada más. Sin embargo si hemos añadido algún campo más a la tabla de usuarios tendremos que actualizar dos métodos del controlador RegisterController: validator y create. En el método validator simplemente tendremos que añadir dicho campo al array de validaciones (solo en el caso que necesitemos validarlo). Y en el método create tendremos que añadir los campos adicionales que deseemos almacenar. El código de este método es el siguiente:

protected function create(array $data) {
    return User::create([
        'name' => $data['name'],
        'email' => $data['email'],
        'phone' => $data['phone'],     // Campo añadido
        'password' => bcrypt($data['password']),
    ]);
}

Como podemos ver utiliza el modelo de datos User para crear el usuario y almacenar las variables que recibe en el array de datos $data. En este array de datos nos llegarán todos los valores de los campos del formulario, por lo tanto, si añadimos más campos al formulario y a la tabla de usuarios simplemente tendremos que añadirlos también en este método.

Es importante destacar que la contraseña se cifra usando el método bcrypt, por lo tanto las contraseñas se almacenaran cifradas en la base de datos. Este cifrado se basa en la clave hash que se general al crear un nuevo proyecto de Laravel (ver capítulo de "Instalación") y que se encuentra almacenada en el fichero .env en la variable APP_KEY. Es importante que este hash se haya establecido al inicio (que no esté vacío o se uno por defecto) y que además no se modifique una vez la aplicación se suba a producción.

Registro manual de un usuario

Si queremos añadir un usuario manualmente lo podemos hacer de forma normal usando el modelo User de Eloquent, con la única precaución de cifrar la contraseña que se va a almacenar. A continuación se incluye un ejemplo de una función que crea un nuevo usuario a partir de los parámetros de entrada recibidos de un formulario:

public function store(Request $request) {
    $user = new User;
    $user->name = $request->input('name');
    $user->email = $request->input('email');
    $user->password = bcrypt( $request->input('password') );
    $user->save();
}

Acceder a los datos del usuario autenticado

Una vez que el usuario está autenticado podemos acceder a los datos del mismo a través del método Auth::user(), por ejemplo:

user = Auth::user();

Este método nos devolverá null en caso de que no esté autenticado. Si estamos seguros de que el usuario está autenticado (porque estamos en una ruta protegida) podremos acceder directamente a sus propiedades:

$email = Auth::user()->email;

Importante: para utilizar la clase Auth tenemos que añadir el espacio de nombres use Illuminate\Support\Facades\Auth;, de otra forma nos aparecerá un error indicando que no puede encontrar la clase.

El usuario también se inyecta en los parámetros de entrada de la petición (en la clase Request). Por lo tanto, si en un método de un controlador usamos la inyección de dependencias también podremos acceder a los datos del usuario:

use Illuminate\Http\Request;

class ProfileController extends Controller {
    public function updateProfile(Request $request) {
        if ($request->user()) {
            $email = $request->user()->email;
        }
    }
}

Cerrar la sesión

Si accedemos a la ruta logout por POST se cerrará la sesión y se redirigirá a la ruta /. Todo esto lo hará automáticamente el método logout del controlador LoginController.

Para cerrar manualmente la sesión del usuario actualmente autenticado tenemos que utilizar el método:

Auth::logout();

Posteriormente podremos hacer una redirección a una página principal para usuarios no autenticados.

Importante: para utilizar la clase Auth tenemos que añadir el espacio de nombres use Illuminate\Support\Facades\Auth;, de otra forma nos aparecerá un error indicando que no puede encontrar la clase.

Comprobar si un usuario está autenticado

Para comprobar si el usuario actual se ha autenticado en la aplicación podemos utilizar el método Auth::check() de la forma:

if (Auth::check()) {
    // El usuario está correctamente autenticado
}

Sin embargo, lo recomendable es utilizar Middleware (como veremos a continuación) para realizar esta comprobación antes de permitir el acceso a determinadas rutas.

Importante: Recuerda que para utilizar la clase Auth tenemos que añadir el espacio de nombres use Illuminate\Support\Facades\Auth;, de otra forma nos aparecerá un error indicando que no puede encontrar la clase.

Proteger rutas

El sistema de autenticación de Laravel también incorpora una serie de filtros o Middleware (ver carpeta app/Http/Middleware y el fichero app/Http/Kernel.php) para comprobar que el usuario que accede a una determinada ruta o grupo de rutas esté autenticado. En concreto para proteger el acceso a rutas y solo permitir su visualización por usuarios correctamente autenticados usaremos el middleware \Illuminate\Auth\Middleware\Authenticate.php cuyo alias es auth. Para utilizar este middleware tenemos que editar el fichero routes/web.php y modificar las rutas que queramos proteger, por ejemplo:

// Para proteger una clausula:
Route::get('admin/catalog', function() {
    // Solo se permite el acceso a usuarios autenticados
})->middleware('auth');

// Para proteger una acción de un controlador:
Route::get('profile', 'ProfileController@show')->middleware('auth');

Si el usuario que accede no está validado se generará una excepción que le redirigirá a la ruta login. Si deseamos cambiar esta dirección tendremos que modificar el método que gestiona la excepción, el cual lo podremos encontrar en App\Exceptions\Handler@unauthenticated.

Si deseamos proteger el acceso a toda una zona de nuestro sitio web (por ejemplo la parte de administración o la gestión de un recurso), lo más cómodo es crear un grupo con todas esas rutas que utilice el middleware auth, por ejemplo:

Route::group(['middleware' => 'auth'], function() {
    Route::get('catalog',        'CatalogController@getIndex');
    Route::get('catalog/create', 'CatalogController@getCreate');
    // ...
});

Si lo deseamos también podemos especificar el uso de este middleware desde el constructor del controlador:

public function __construct() {
    $this->middleware('auth');
}

Sin embargo, lo más recomendable es indicarlo desde el fichero de rutas pues así tendremos todas las rutas y filtros centralizados en un único fichero.

Last updated