Tempo di lettura: 75 minuti
Laravel: Autenticazione
L’autenticazione è una delle attività più comuni di un’applicazione. Vogliamo sempre consentire solo a determinati tipi di utenti di accedere a determinate aree. Ad esempio, il pannello amministrativo deve essere accessibile solo agli utenti registrati.
In questa sezione impareremo come creare una pagina di registrazione e una pagina di accesso. Successivamente, impareremo come limitare l’accesso all’area di amministrazione.
Registra un nuovo utente in Laravel
Per iniziare, dovremo costruire un’API che gestirà una richiesta di registrazione, inviata dalla nostra app Angular.
Vorrei mettere tutte le azioni in un nuovo controller chiamato AuthController :
Nota: possiamo UsersController o qualsiasi altro controller desiderato.
per cui (su laravel) da terminale digitiamo:
php artisan make:controller AuthController
Successivamente, apri api.php e aggiungi una nuova rotta chiamata register :
Route::group(['prefix' => 'v1', 'middleware' => 'cors'], function(){ Route::resource('users', UserController::class); Route::post('register', 'App\Http\Controllers\AuthController@postRegister'); });
Apri AuthController e aggiungi una nuova azione chiamata postRegister :
<?php namespace App\Http\Controllers; use Illuminate\Http\Request; use Illuminate\Support\Facades\Response; use App\Models\User; use Illuminate\Support\Facades\Validator; class AuthController extends Controller { public function postRegister(Request $request) { $validator = Validator::make($request->all(), [ 'email' => 'required|email|unique:users,email', 'name' => 'required|min:2', 'password' =>'required|min:8' ]); if ($validator->fails()) { $message = ['errors' => $validator->messages()->all()]; $response = Response::json($message,202); } else { $user = new User(array( 'email' => trim($request->email), 'name' => trim($request->name), 'password' => bcrypt($request->password), )); $user->save(); $message = 'The user has been created successfully'; $response = Response::json([ 'message' => $message, 'user' => $user, ], 201); } return $response; } }
Stiamo usando User , Validator e Response qui, quindi importiamoli. Trova:
class AuthController extends Controller
Aggiungi sopra:
use App\Models\User; use Illuminate\Support\Facades\Response; use Illuminate\Support\Facades\Validator;
Potresti notare che abbiamo appena creato alcune regole di convalida . Se non hai familiarità con queste regole, dai un’occhiata alla documentazione di Laravel .
Se la convalida non riesce , verrà generata una risposta di errore per avvisare gli utenti. Nel caso in cui questo fosse valido , verrà generata una risposta positiva e verrà creato un nuovo utente.
Registra un nuovo utente in Angular
Come di certo immaginerete, dovremo creare un nuovo componente chiamato componente Register , questo sarà simile al componente Admin-create-user.ts.
Verrà creato nella directory principale in quanto deve essere accessibile a tutti, quindi da terminale:
ng g c components/register
Apri app-routing.modules.ts , aggiungi un nuovo percorso:
const routes: Routes = [ { path: '', redirectTo: '/home', pathMatch: 'full'}, {path: 'home', component: HomeComponent}, {path: 'about', component: AboutComponent}, {path: 'contact', component: ContactComponent}, { path: 'admin', component: DashboardComponent, children: adminRoutes}, { path: 'register', component: RegisterComponent}, ];
ecco il file app-routing.modules.ts aggiornato:
import { NgModule } from '@angular/core'; import { Routes, RouterModule } from '@angular/router'; import {AboutComponent} from './components/about/about.component'; import {ContactComponent} from './components/contact/contact.component'; import {HomeComponent} from './components/home/home.component'; import {adminRoutes} from './admin/admin.route'; import {DashboardComponent} from './admin/dashboard.component'; import {RegisterComponent} from './components/register/register.component'; const routes: Routes = [ { path: '', redirectTo: '/home', pathMatch: 'full'}, {path: 'home', component: HomeComponent}, {path: 'about', component: AboutComponent}, {path: 'contact', component: ContactComponent}, { path: 'admin', component: DashboardComponent, children: adminRoutes}, { path: 'register', component: RegisterComponent}, ]; @NgModule({ imports: [RouterModule.forRoot(routes)], exports: [RouterModule] }) export class AppRoutingModule { }
Poiché questo modulo verrà inserito dagli utenti ospiti, dovremmo aggiungere un campo password di input aggiuntivo chiamato password_confirmation per assicurarci che la password sia corretta. Inoltre, aggiungeremo una nuova regola di convalida personalizzata chiamata samePassword per convalidare i campi.
Ecco il register.component.ts :
register.component.ts
import { Component, OnInit } from '@angular/core'; import {FormBuilder, FormControl, FormGroup, Validators} from '@angular/forms'; import {User} from '../../models/user'; import {Router} from '@angular/router'; import {CustomValidators} from '../../classe/custom-validators'; import {AuthService} from '../../services/auth.service'; @Component({ selector: 'app-register', template: ` <div class="container"> <div class="col-md-12"> <div> <a [routerLink]="['/']" class="btn btn-light"> Back</a> </div> </div> <div class="col-md-12"> <div class="card card-body bg-light"> <form novalidate class="form-horizontal" (ngSubmit)="registerUser()" [formGroup]="userForm"> <fieldset> <legend>Registrati</legend> <div class="form-group"> <label for="email" class="col-lg-2 control-label">Email</label> <div class="col-lg-10"> <input type="email" class="form-control" id="email" name="email" placeholder="Inserisci email" formControlName="email"> <div *ngIf="userForm.get('email').dirty && userForm.get('email').hasError('invalidEmail')" class="alert alert-danger">Formato email non valido</div> <div *ngIf="userForm.get('email').dirty && userForm.get('email').hasError('required')" class="alert alert-danger">Email richiesta</div> <div *ngIf="userForm.get('email').dirty && userForm.get('email').hasError('minlength')" class="alert alert-danger">Devi scrivere almeno 3 caratteri</div> </div> </div> <div class="form-group"> <label for="name" class="col-lg-2 control-label">Full name</label> <div class="col-lg-10"> <input type="text" class="form-control" id="name" name="name" placeholder="Inserisci nome e cognome" formControlName="name"> <div *ngIf="userForm.get('name').dirty && userForm.get('name').hasError('required')" class="alert alert-danger">Nome richiesto</div> <div *ngIf="userForm.get('name').dirty && userForm.get('name').hasError('minlength')" class="alert alert-danger">Il nome deve contenere almeno 3 caratteri</div> </div> </div> <div class="form-group"> <label for="password" class="col-lg-2 control-label">Password</label> <div class="col-lg-10"> <input type="password" class="form-control" id="password" name="password" placeholder="Password" formControlName="password"> <div *ngIf="userForm.get('password').dirty && userForm.get('password').hasError('required')" class="alert alert-danger">Password richiesta</div> <div *ngIf="userForm.get('password').dirty && userForm.get('password').hasError('minlength')" class="alert alert-danger">La password deve contenere almeno 8 caratteri</div> <div *ngIf="userForm.get('password').dirty && userForm.get('password').hasError('hasNumber')" class="alert alert-danger">La password deve contenere anche numeri</div> <div *ngIf="userForm.get('password').dirty && userForm.get('password').hasError('hasCapitalCase')" class="alert alert-danger">La password deve contenere anche caratteri maiuscoli</div> <div *ngIf="userForm.get('password').dirty && userForm.get('password').hasError('hasCamelCase')" class="alert alert-danger">La password deve contenere anche caratteri minuscoli</div> <div *ngIf="userForm.get('password').dirty && userForm.get('password').hasError('hasSpecialCharacters')" class="alert alert-danger">La password deve contenere almeno un carattere speciale: @ # | ! * </div> </div> </div> <div class="form-group"> <label for="confirmPassword" [ngClass]="userForm.controls['confirmPassword'].dirty ? 'text-danger' : ''">Confirm Password:</label> <div class="col-lg-10"> <input id="confirmPassword" formControlName="confirmPassword" type="password" class="form-control" [ngClass]="userForm.controls['confirmPassword'].invalid ? 'is-invalid' : ''"> <label class="text-danger" *ngIf="userForm.controls['confirmPassword'].dirty && userForm.controls['confirmPassword'].hasError('required')"> Password is Required! </label> <label class="text-danger" *ngIf="userForm.controls['confirmPassword'].dirty && userForm.controls['confirmPassword'].hasError('NoPassswordMatch')"> Password do not match </label> </div> </div> <div class="form-group"> <div class="col-lg-10 col-lg-offset-2"> <a [routerLink]="['/']" class="btn btn-light"> Cancel</a> <button type="submit" class="btn btn-primary" [disabled]="userForm.invalid">Create</button> </div> </div> </fieldset> </form> </div> </div> </div> `, styleUrls: ['./register.component.css'] }) export class RegisterComponent implements OnInit { userForm: FormGroup; user: User; static isValidEmail(control: FormControl): any { const emailRegexp = /^[a-z0-9!#$%&'*+\/=?^_`{|}~.-]+@[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*$/i; return emailRegexp.test(control.value) ? null : { invalidEmail: true }; } constructor(fb: FormBuilder, private authService: AuthService, private router: Router) { this.userForm = fb.group({ email: fb.control('', [Validators.required, Validators.minLength(3), RegisterComponent.isValidEmail]), name: fb.control('', [Validators.required, Validators.minLength(3)]), password: fb.control('', [ Validators.required, Validators.minLength(8), Validators.maxLength(32), CustomValidators.patternValidator(/\d/, { hasNumber: true }), CustomValidators.patternValidator(/[A-Z]/, { hasCapitalCase: true }), CustomValidators.patternValidator(/[a-z]/, { hasSmallCase: true }), CustomValidators.patternValidator(/[@#{}|!()*{}]/, { hasSpecialCharacters: true }), ]), confirmPassword: [null, Validators.compose([Validators.required])] }, { validator: CustomValidators.passwordMatchValidator } ); } ngOnInit(): void { } registerUser(): void { console.log(this.userForm.value); } }
ora cliccate su http://localhost:4200/register
inserite i dati ed avrete :
ora realizzate il service auth:
ng g s services/auth
quindi aprio ed aggiorna come segue:
import { Injectable } from '@angular/core'; import {Observable, throwError} from 'rxjs'; import {User} from '../models/user'; import {HttpClient, HttpErrorResponse} from '@angular/common/http'; import {catchError, map} from 'rxjs/operators'; @Injectable({ providedIn: 'root' }) export class AuthService { user: User; constructor(private http: HttpClient) { } register(user): Observable<any> { return this.http.post('http://127.0.0.1:8000/api/v1/register', user) .pipe( map((response) => response), catchError(this.errorHandler) ); } errorHandler(error: HttpErrorResponse): any { return throwError(error.error || {message: 'Server Error'}); } }
quindi prova ad effettuare una registrazione di un nuovo utente http://localhost:4200/register ed accertati che l’utente venga inserito nel db.
Visualizzazione di più errori
Il nostro modulo di registrazione non è ancora perfetto. Non visualizza gli errori dal server quando il Laravel Validator non riesce. Per questo motivo, se proviamo a registrare un nuovo account utilizzando un’e-mail esistente, non verrà creato un nuovo utente, ma non vedremo alcun errore.
Per correggere questo bug, apri register.component.ts e aggiungi alcune nuove variabili:
Trova:
export class RegisterComponent implements OnInit {
Aggiungi sotto:
status: string; message: string; errors: any;
La variabile degli errori conterrà uno o più errori, perché restituiamo un array di errori in Laravel:
AuthController.php (file Laravel)
if ($validator->fails()) { $message = ['errors' => $validator->messages()->all()]; $response = Response::json($message,202); }
Una volta che abbiamo tutte le variabili, possiamo aggiornare il metodo registerUser () come segue:
register.component.ts
registerUser(): void { this.authService.register(this.userForm.value) .subscribe( response => { const user = response.user; if (user) { console.log('A new user has been created'); this.status = 'success'; this.message = response[`message`]; this.router.navigate(['/admin']); } else { this.errors = response.errors; } }, error => console.log(error)); }
Come vedi, se la risposta contiene informazioni sull’utente, verrà visualizzato un messaggio di successo. Altrimenti, assegniamo semplicemente gli errori della risposta alla variabile errors che abbiamo appena creato.
Per visualizzare più errori, useremo ngFor :
Apri register.component.ts e trova:
<div class="card card-body bg-light">
aggiungi sopra:
<div *ngIf="status=='success'" class="alert alert-success" role="alert"> {{ message }} </div> <div *ngFor="let error of errors"> <div class="alert alert-danger" role="alert"> {{ error }} </div> </div>
Ora se provi a registrare un nuovo account utilizzando un’e-mail registrata, dovresti vedere un errore:
Nota: poiché qui abbiamo solo un errore, ne vediamo solo uno. Se il server restituisce più errori, li vedremo tutti.
È così che visualizziamo più errori!