Come usare le Route Guards con il routing di Angular

Immagina di voler mettere in sicurezza la tua applicazione Angular e di voler gestire l’accesso ad una serie di route solo agli utenti autorizzati: come fare?
Basta usare le Route Guard. π
TOC
Cosa sono le Route Guard
Angular Route Guards ci aiuta a impedire all’utente di accedere a determinate parti delle applicazioni in condizioni specifiche. Possiamo anche utilizzarlo per eseguire alcune azioni prima di navigare verso un percorso o di mandare l’utente in un’altra pagina.
Queste risorse funzionano da controllo sull’accesso e supportano diversi casi d’uso: una “guardia” puΓ² attivare o disattivare l’accesso a una specifica rotta, oppure valutare l’accesso a un componente “figlio” di quello richiesto. Vediamo qualche esempio!
Come funzionano
Per il momento, per spiegarne il funzionamento in Angular, ci concentreremo sul tipo piΓΉ comune: CanActivate.
Per prima cosa, creiamo una route guard
che, tramite l’autenticazione verifichi se l’utente ha effettuato l’accesso.
// auth.guard.ts
import { Injectable } from '@angular/core';
import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, Router } from '@angular/router';
import { AuthService } from './auth.service';
@Injectable({
providedIn: 'root'
})
export class AuthGuard implements CanActivate {
constructor(private authService: AuthService, private router: Router) {}
canActivate(
next: ActivatedRouteSnapshot,
state: RouterStateSnapshot): boolean {
if (this.authService.isAuthenticated()) {
return true;
} else {
this.router.navigate(['/login']);
return false;
}
}
}
Successivamente, andiamo a lavorare sulla configurazione del routing, specificando le “regole” di accesso: per esempio, in questo caso lasciamo che l’accesso alla pagina principale sia aperto a tutti gli utenti, mentre il componente dashboard
solo a chi ha eseguito il login.
Questo viene verificato tramite l’AuthGuard definito in precedenza che, restituendo un valore booleano, specifica ad Angular se l’utente puΓ² accedere o meno alla pagina richiesta.
L’ultima route il cui path Γ¨ **
serve a specificare che qualsiasi richiesta di accesso a rotte diverse da quelle specificate o richieste che provengono da utenti senza i permessi, devono rimandare al componente PageNotFound
:
// app-routing.module.ts:
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { HomeComponent } from './home.component';
import { DashboardComponent } from './dashboard.component';
import { AuthGuard } from './auth.guard';
import { PageNotFoundComponent } from './pageNotFound/pageNotFound.component';
const routes: Routes = [
{ path: '', component: HomeComponent },
{ path: 'dashboard', component: DashboardComponent, canActivate: [AuthGuard] },
{ path: 'login', component: LoginComponent },
{ path: 'pageNotFound', component: PageNotFound },
{ path: '**', redirectTo: 'pageNotFound' }
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule {}
Un esempio di AuthService
in Angular Γ¨ il seguente, che include il metodo isAuthenticated
. Questo servizio gestisce l’autenticazione utente e fornisce metodi per controllare lo stato di autenticazione.
// auth.service.ts
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Router } from '@angular/router';
import { BehaviorSubject, Observable } from 'rxjs';
import { tap } from 'rxjs/operators';
@Injectable({
providedIn: 'root'
})
export class AuthService {
private apiUrl = 'https://mybackend/auth';
private userSubject: BehaviorSubject<any>;
public user: Observable<any>;
constructor(private http: HttpClient, private router: Router) {
this.userSubject = new BehaviorSubject<any>(JSON.parse(localStorage.getItem('user') || '{}'));
this.user = this.userSubject.asObservable();
}
login(username: string, password: string): Observable<any> {
return this.http.post<any>(`${this.apiUrl}/login`, { username, password })
.pipe(tap(user => {
localStorage.setItem('user', JSON.stringify(user));
this.userSubject.next(user);
}));
}
isAuthenticated(): boolean {
const user = this.userSubject.value;
return user && user.token ? true : false;
}
logout(): void {
localStorage.removeItem('user');
this.userSubject.next(null);
this.router.navigate(['/login']);
}
}
Giusto per dare qualche informazione di piΓΉ sugli oggetti utilizzati in questo esempio, parliamo di BehaviorSubject
: userSubject
Γ¨ un BehaviorSubject
che contiene le informazioni dell’utente corrente. Si inizializza con qualsiasi dato utente esistente dal localStorage.
Grazie al metodo di login che invia una richiesta all’API con le credenziali dell’utente, in caso di successo, il servizio memorizza i dati utente restituiti (incluso un token JWT) nello storage locale e aggiorna l’oggetto userSubject
.
Allo stesso modo, il metodo di logout cancella i dati dell’utente dallo storage locale e torna alla pagina di login una volta che l’utente si Γ¨ disconnesso.