View on GitHub

apunts

Apunts DWES

Control d’usuaris

A l’hora d’afegir autenticació d’usuaris en les nostres aplicacions Laravel, hem de tindre en compte que existeixen dos possibles escenaris:

En el primer cas, haurem d’implementar una autenticació més o menys “manual”, encara que molt senzilla. En el segon cas, podem especificar unes opcions en crear el projecte que ens facilitaran molt la incorporació del login, i fins i tot del registre de nous usuaris. Veurem cada cas per separat.

Configuració general de l’autenticació

En l’arxiu config/auth.php es disposa d’algunes opcions de configuració generals d’autenticació. Aquesta autenticació en Laravel es recolza en dos elements: els guards i els providers.

Haurem de modificar en l’arxiu la referència a la taula on emmagatzemarem els usuaris (per defecte es fa referència a una taula anomenada users ) i/o al model associat (per defecte, la classe User ). Així que convindrà modificar els noms d’aquests dos elements en la secció providers , així com la ubicació (namespace) del model d’usuari, si escau. Per exemple:

...
'providers' => [
'users' => [
'driver' => 'eloquent',
'model' => App\Models\Usuario::class,
],
// 'users' => [
// 'driver' => 'database',
// 'table' => 'usuarios',
// ],
],

Notar que la secció providers disposa de dos proveïdors d’autenticació: un (el que està habilitat) està basat en Eloquent, i fa ús del model d’usuaris que hàgem definit. L’altre (que apareix comentat) no utilitza Eloquent, sinó del query builder contra la pròpia base de dades. Si preferim aquesta segona opció, haurem de comentar la primera i deixar habilitada la segona. També és possible deixar habilitats múltiples providers, cadascun amb un nom diferent, i assignar-lo a múltiples guards.

El model o la taula users

Si triem el provider basat en Eloquent, haurem de tindre un model d’usuaris al qual accedir. En el cas de la nostra aplicació de blog, disposem ja d’un model creat en App\Models\Usuari , per la qual cosa l’exemple anterior ens serviria per a establir aquest model com el model d’usuaris per defecte.

Si optem per utilitzar el query builder en lloc de Eloquent, haurem de tindre una taula en la base de dades on estiguen les dades dels usuaris. En el nostre cas, també disposem d’aqueixa taula usuaris, per la qual cosa podríem emprar aquesta altra opció per a autenticar usuaris si volguérem.

No obstant això, ens valdrem de Eloquent per a l’autenticació. En qualsevol cas, com veurem a continuació, serà convenient que els passwords dels usuaris estiguen encriptats mitjançant bcrypt, que és el mecanisme d’encriptació per defecte que utilitza Laravel.

Afegir autentiació a un projecte existent

Per a afegir autenticació a un projecte Laravel ja existent que no dispose d’aquests mecanismes,seguirem aquests passos:

  1. Definirem un formulari de *login
  2. Definirem un nou controlador que s’encarregue de gestionar el *login: tant de mostrar el formulari quan l’usuari no estiga autenticat com de validar les seues credencials quan les envie
  3. Afegirem les rutes pertinents en l’arxiu routes/web.php tant per al formulari de *login com per a l’autenticació posterior
  4. Protegirem les rutes que siguen d’accés restringit
  5. Opcionalment, podem afegir també una opció de logout.
El formulari de login

Definirem un formulari de login en la vista **resources/views/auth/login.blade.php , perquè l’usuari especifique el seu login i el seu password. També deixarem una zona per a mostrar un possible missatge d’error si l’autenticació no ha sigut reeixida:

@extends('plantilla')
@section('titulo', 'Login')
@section('contenido')
	<h1>Login</h1>
	@if (!empty($error))
		<div class="text-danger">
			
		</div>
	@endif
<form action="" method="POST">
	@csrf
	<div class="form-group">
		<label for="login">Login:</label>
		<input type="text" class="form-control"
name="login" id="login" />
	</div>
	<div class="form-group">
		<label for="password">Password:</label>
		<input type="password" class="form-control"
name="password" id="password" />
	</div>
	<input type="submit" name="enviar" value="Enviar"
class="btn btn-dark btn-block">
</form>
@endsection
El controlador de Login

Crearem un nou controlador que s’encarregue de gestionar tota l’autenticació:

php artisan make:controller LoginController

Dins, definim una funció que s’encarregarà de mostrar el formulari anterior:

public function loginForm()
{
	return view('auth.login');
}

I afegirem una segona funció que s’encarregue de validar les credencials enviades per l’usuari. Per a això,farem ús del facade d’autenticació, existent en Illuminate\Support\Facades\Auth . Recorda que un facade és bàsicament un element que proporciona accés a una sèrie de mètodes estàtics d’utilitat, en aquest cas per a autenticar usuaris.

namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;

class LoginController extends Controller
{
	...
	public function login(Request $request)
	{
		$credenciales = $request->only('login', 'password');
		if (Auth::attempt($credenciales))
		{
			// Autenticación exitosa
			return redirect()->intended(route('movies.index'));
		} else {
			$error = 'Usuario incorrecto';
			return view('auth.login', compact('error'));
		}
	}
}

El mètode attempt accepta una sèrie de parells clau-valor com a primer paràmetre. En aquest cas, li passem un només parell format pel login (o l’e-mail, depenent del camp que usem per a autenticar) i el password rebuts en la petició. Això servirà per a localitzar a l’usuari per la clau , i comprovar si té el valor associat (el password). En el cas dels passwords, Laravel automàticament els encripta en format bcrypt, per la qual cosa hem de cerciorar-nos que el password està encriptat en aqueix format en la base de dades.

D’altra banda, el mètode intended tracta d’enviar a l’usuari a la ruta a la qual intentava accedir abans que se li sol·licitara autenticació. Li passem com a paràmetre una ruta per defecte en el cas que la destinació prevista no estiga disponible.

Les rutes associades

Finalment, hem de definir les rutes tant per a mostrar el formulari (per get) com per a recollir les credencials i validar a l’usuari (per post).

Route::get('login', [LoginController::class, 'loginForm'])->name('login');
Route::post('login', [LoginController::class, 'login']);
Redirecció en cas d’error

Quan es detecta que un usuari no autenticat intenta accedir a una ruta protegida, automàticament se li redirigeix a la ruta nomenada com login (com la que hem definit prèviament), on veurà el formulari d’accés. Si volem canviar el nom de la ruta a la qual redirigir (en el cas que no vulguem que siga login), hem de modificar el mètode redirectTo en el middleware d’autenticació app/Http/Middleware/Authenticate.php :

protected function redirectTo($request)
{
...
return route('login');
}
Protegir les rutes d’accés restringit

Ara que ja tenim definit el mecanisme de login (controlador amb mètode d’autenticació, formulari de login i ruta associada), podem protegir aquelles rutes o enllaços que vulguem que siguen d’accés restringit. Per exemple, podem fer que les operacions de creació, esborrat i edició de videos (funcions create , store , edit , update i destroy ) només estiguen disponibles per a usuaris autenticats. Això pot fer-se de diverses formes.

Si tenim una ruta de recursos ( Route::resource ) en l’arxiu routes/web.php , llavors l’opció més còmoda és definir un constructor en el controlador associat (en aquest cas,MovieController ), i especificar quines funcions volem protegir, bé amb only o amb except (en aquest últim cas, es protegiran totes les rutes excepte les indicades en la llista):

class LibroController extends Controller
{
	public function __construct()
	{
		$this->middleware('auth',['only' => ['create', 'store', 'edit', 'update', 'destroy']]);
	}
...

Si definim les rutes soltes, podem emprar el mètode middleware per a indicar en cadascuna si volem que s’aplique el middleware d’autenticació:

Route::get('prueba', [PruebaController::class, 'create'])->middleware('auth');
Detectar en les vistes l’usuari autenticat

Pot ser molt necessari detectar en una vista si l’usuari s’ha autenticat o no, bé per a mostrar certs controls (per exemple, enllaços per a crear llibres), o per a carregar informació pròpia de l’usuari (per exemple, posts creats per l’usuari que ha entrat en un blog). Per exemple, d’aquesta manera podem modificar el menú de navegació ( **resources/views/partials/nav.blade.php ) perquè mostre l’enllaç de crear nou nova película només si l’usuari s’ha autenticat:

@if(Auth()::check())
	<li class=" nav-item">
<a class="nav-link" href="">Nova pelicula</a
</li>
@endif

Podem emprar el mètode Auth::guest() si volem comprovar si l’usuari encara NO s’ha autenticat (per exemple, per a mostrar-li l’enllaç a login), i el mètode Auth::check() per a comprovar si SI està autenticat (per a mostrar-li, per exemple, les opcions restringides). De manera anàloga, el mètode Auth::user() obté l’objecte de l’usuari autenticat, amb el que podem accedir als seus atributs:

Bienvenido/a 
Implementació del logout

Per a implementar el logout, n’hi ha prou amb cridar al mètode logout del facade Auth utilitzat anteriorment, en el mètode que es vaja a encarregar d’aqueixa tasca. Ho podem afegir en el mateix controlador anterior:

namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
class LoginController extends Controller
{
	...
	public function logout()
	{
		Auth::logout();
		// ... Renderizar la vista deseada
	}

També farà falta definir la ruta associada en routes/web.php :

Route::get('logout', [LoginController::class, 'logout'])->name('logout');

Òbviament, també serà necessari afegir un enllaç per a fer logout en alguna part. Podem posar-ho en el menú de navegació (arxiu resources/views/partials/nav.blade.php , quan detectem que l’usuari està autenticat):

@if(Auth::check())
	<li class=" nav-item">
		<a class="nav-link" href="">Nuevo libro</a
	</li>
	<li class="nav-item">
		<a class="nav-link" href="">Logout</a>
	</li>
@endif

Crear un projecte desde cero amb l’autenticació incorporada

Laravel també ofereix l’opció de crear un projecte des de zero incorporant mecanismes d’autenticació d’usuaris des del principi. Aquesta opció ha variat amb el pas de les versions de Laravel,però bàsicament es conserva una mateixa essència: quan creem el projecte, podem deixar ja establit el model d’usuaris, i mecanismes per a registrar i autenticar usuaris en l’aplicació.

Per a crear un projecte amb aquesta infraestructura ja definida executem el comando laravel new amb l’opció –jet en Laravel 8.

laravel new nombre_proyecto --jet

Això crearà el projecte incorporant la infraestructura de gestió d’usuaris, juntament amb formularis bàsics per a registre i login. En el cas de Laravel 8 s’empra JetStream, la nova utilitat incorporada en aquesta versió per a proporcionar l’esquelet bàsic o scaffolding d’autenticació. Una de les principals diferències entre totes dues versions és que en Laravel 7 s’empra Bootstrap per a l’aparença o el disseny web, i en Laravel 8 s’empra Tailwind. Ho podem comprovar donant una ullada al contingut de la carpeta views , on ja tindrem unes quantes vistes preparades per a registre, login, recuperació de contrasenya, etc:

Evidentment, abans de poder fer res necessitem tindre una base de dades creada. Recorda crear-la i modificar les dades de connexió en l’arxiu .env del teu projecte, i també executar les migracions predefinides en el projecte amb php artisan migrate:fresh.

Canviar l’idioma

Un dels inconvenients que tenim en utilitzar aquest scaffolding és que tots els enllaços i camps de formularis que s’han creat vénen amb textos en anglés. Podem afegir missatges en altres idiomes amb dos passos senzills:

'locale' => 'es',

Si els donem una ullada, podem veure que contenen les traduccions a l’espanyol de diferents missatges de diferents seccions de la web. Amb això ja podrem veure els continguts de la web en espanyol. Aquesta “màgia” es deu al fet que en les vistes que hem carregat, els missatges s’obtenen directament d’aquesta carpeta resources/lang per a l’idioma especificat en config/app.php . Per exemple, així es mostra el missatge Forgot Your Password? original en el formulari de login:

<a class="btn btn-link" href="">

</a>

La instrucció __() cerca la clau que li passem (Forgot Your Password?) en els arxius de configuració en anglés, i si no la troba, posa directament la clau sense més. Si busquem aquesta mateixa clau en l’arxiu es.json que hem descarregat, podem veure per què text se substituirà quan canviem l’idioma a espanyol:

"Forgot Your Password?": "¿Olvidaste tu contraseña?",

Altres consideracions

La incorporació del scaffolding d’autenticació, com podem comprovar, estalvia molta faena a l’hora de definir els mecanismes de registre i login d’usuaris en el sistema. No obstant això, queden algunes tasques pendents que poden requerir una configuració addicional, i que no veurem en aquest curs per falta de temps.

Una d’elles, per exemple, és l’opció de recuperar contrasenya quan punxem en l’enllaç de Vas oblidar la teua contrasenya?. En principi, i atés que ens registrem amb un e-mail com login, ens demana que facilitem aquest e-mail per a enviar-nos un enllaç per a restablir la contrasenya. No obstant això, hem de configurar apropiadament el correu SMTP per a poder enviar el missatge. Per a això, necessitem un compte origen, i depenent del servidor de correu on el tinguem creat (Gmail, Outlook, etc), la configuració és diferent. Ací, per exemple, explica els passos a seguir per a configurar com a compte emissora una de Gmail.

Definir roles. Ús de middleware

Per a poder definir rols per als diferents usuaris de la nostra aplicació, òbviament hem de començar per definir un nou camp en la taula d’usuaris per a emmagatzemar aquest rol.

Després, per a protegir certes rutes en funció dels rols, podem ocultar l’enllaç en les vistes amb una simple comprovació. Per exemple, assumint que el camp dels rols es diu rol:

@if (Auth::user()->rol === 'admin')
	// Mostrar contenido
@endif

No obstant això, si accedim a la URL sense passar per l’enllaç, podrem veure el contingut. Devem novament incorporar el middleware auth al controlador que corresponga (si no ho està ja), per a protegir l’accés general per a usuaris autenticats. A més, hem de definir un middleware propi que verifique el rol de l’usuari logueado. Podem crear-ho amb aquest comando:

php artisan make:middleware RolCheck

En aquest cas hem anomenat al middleware RolCheck , però el nom pot ser el que vulguem. Aquest middleware es crearà en la carpeta App\Http\Middleware . Hem d’editar el seu mètode handle per a verificar que els usuaris són de tipus “admin”:

public function handle($request, Closure $next, $rol)
{
	if (Auth::user()->rol === $rol)
		return $next($request);
	else
		return redirect('/');
}

Després de definir el middleware, ho registrem en l’arxiu App/Http/Kernel.php (en l’apartat de routeMiddleware):

protected $routeMiddleware = [
...
'roles' => \App\Http\Middleware\RolCheck::class

Finalment, ho carreguem en el constructor del nostre controlador. Podem incloure amb except i only restriccions sobre quins mètodes del controlador es veuran afectats o no pel middleware.

public function __construct()
{
	$this->middleware(['auth', 'roles:admin']);
}

En aquest exemple, hem mapatge el middleware amb l’àlies rols en l’arxiu Kernel.php , i el que hi ha després dels dos punts és el paràmetre extra que té el mètode handle del middleware (el rol a comprovar). En el cas de voler passar més paràmetres, es pot fer separats per comes.

Sobre el concepte de middleware

Hem comentat breument el concepte de middleware associat tant al mecanisme d’autenticació com a la classe “extra” que podem crear per a comprovar rols. En general, un middleware és un fragment de codi (normalment una funció) que s’executa enmig d’un procés. En aquest cas, s’executa des que es rep la petició fins que s’emet la resposta, i permet alterar aqueix flux normal, fent certes comprovacions sobre la petició. Per exemple, com és el cas, verificar que l’usuari té els permisos adequats abans d’emetre una resposta o una altra.