В этой статье разберемся в тонкостях аутентификации и авторизации пользователей в Angular 16, уделив особое внимание использованию JSON Web Tokens (JWT). JWT стал популярным выбором для реализации аутентификации и авторизации благодаря своей простоте, масштабируемости и совместимости.

Итак, погрузимся в тему.

1. Сервис аутентификации

Начнем с создания сервиса аутентификации (AuthService), который будет контролировать вход в систему и выход из нее, а также управлять токенами.

import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';

export class AuthService {
constructor(private http: HttpClient) {}

isLoggedIn: boolean = false;

login(userDetails: { username: string; password: string }): Observable<boolean> {
return this.http.post<any>('http://examples/api/login', userDetails)
.pipe(
map(response => {
localStorage.setItem('JWT_Token', response.token);
this.isLoggedIn = true;
return true;
}),
catchError(error => {
console.log(error);
this.isLoggedIn = false;
return of(false);
})
);
}

logout(): void {
localStorage.removeItem('JWT_Token');
this.isLoggedIn = false;
}

isAuthenticated(): boolean {
return this.isLoggedIn;
}
}

В этом сервисе метод login отправляет POST-запрос на конечную точку login API с учетными данными пользователя. После успешной проверки учетных данных обратно отправляется ответ об успехе (success), содержащий JWT-токен, после чего сохраняем JWT в локальном хранилище и обновляем статус аутентификации. Метод logout очищает JWT-токен из локального хранилища и сбрасывает статус аутентификации. Метод isAuthenticated возвращает сведения о том, аутентифицирован ли пользователь в данный момент.

2. Сервис защиты авторизации

Сервис защиты авторизации (AuthGuardService) обеспечивает безопасную навигацию, ограничивая доступ к маршрутам на основе статуса аутентификации пользователя.

import { CanActivateFn, Router } from '@angular/router';
import { AuthService } from './auth.service';
import {inject} from "@angular/core";

export const AuthGuardService : CanActivateFn () => {

let isauthenticated = inject(AuthService).isAuthenticated()
let router = inject(Router)

if (isauthenticated) {
return true;
} else {
router.navigate(['/Login']);
return false;
}
}

В процессе реализации функции CanActivateFn перехватываются попытки активации маршрута. Изначально определяем функцию AuthGuardService типа canActivateFn. Затем присваиваем экземпляр метода isAuthenticated из сервиса авторизации переменной isauthenticated, а Router  —  переменной router. Затем устанавливаем условие: если isAuthenticated (пользователь аутентифицирован), то возвращается true и навигация продолжается. В противном случае возвращается false, отказывая пользователю в доступе к защищенному маршруту и перенаправляя его на страницу ‘login’ с помощью сервиса router.

3. Сервис-перехватчик

Этот сервис (JwtInterceptor) перехватывает исходящие HTTP-запросы и добавляет JWT в заголовок Authorization перед отправкой на сервер.

import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http';

export class JwtInterceptor implements HttpInterceptor {

intercept(req: HttpRequest<any>, next: HttpHandler) {
const token = localStorage.getItem('JWT_Token');
if (token) {
const authReq = req.clone({
setHeaders: {
Authorization: `Bearer ${token}`
}
});
return next.handle(authReq);
} else {
return next.handle(req);
}
}
}

Начинаем с импорта необходимых модулей из пакета Angular HTTP. Затем определяем класс JWTInterceptor, который реализует интерфейс HttpInterceptor. Внутри этого класса определяем метод intercept, принимающий два параметра: req (HTTP-запрос) и next (HTTP-обработчик для следующего перехватчика в цепочке).

На начальном этапе извлекаем JWT (JSON Web Token) из локального хранилища, которое ранее было сохранено сервисом AuthService. Проводим валидацию наличия токена и, если он существует, создаем модифицированную копию исходящего запроса (authReq) с добавленным заголовком Authorization, содержащим токен JWT. Этот модифицированный запрос, теперь включающий токен, передается на сервер.

В результате сервер, распознав токен, предоставляет доступ к своим защищенным ресурсам. В случае, если токен не найден, исходный запрос отправляется без изменений. Независимо от этого, мы обеспечиваем передачу запроса на сервер, вызывая next.handle(request). Этот перехватчик играет важную роль в дополнении исходящих запросов необходимыми учетными данными для авторизации, обеспечивая безопасное взаимодействие с сервером.

4. Добавление сервиса защиты авторизации в Approuting.module.ts

import { Routes } from '@angular/router';
import { HomeComponent } from './home.component';
import { ProductsComponent } from './products.component';
import { AuthGuardService } from './auth-guard.service';

const routes: Routes = [
{ path: 'home', component: HomeComponent, canActivate: [AuthGuardService] },
{ path: 'products', component: ProductsComponent, canActivate: [AuthGuardService] }
];

@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule {}

Определяем маршруты для приложения с помощью массива Routes. Каждый маршрут  —  это объект со свойствами, определяющими путь и компонент, который будет отображаться при обращении к этому пути. Свойство canActivate используется для защиты маршрутов, следя за тем, чтобы выполнялись определенные условия перед разрешением доступа. В данном случае созданный ранее AuthGuardService импортируется таким образом, чтобы он реализовывал интерфейс CanActivate для обеспечения функциональности защиты маршрутов.

5. Создание перехватчика в app.module.ts

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { AuthGuardService } from './auth.guard';
import { JwtInterceptor } from './jwt.interceptor';

@NgModule({
declarations: [AppComponent],
imports: [BrowserModule, HttpClientModule, AppRoutingModule],
providers: [
AuthGuardService,
{ provide: HTTP_INTERCEPTORS, useClass: JwtInterceptor, multi: true }
],
bootstrap: [AppComponent]
})
export class AppModule {}

В этом модуле предоставляем класс HeadersInterceptor в качестве перехватчика, используя токен HTTP_INTERCEPTORS. Опция multi: true гарантирует, что перехватчик будет добавлен к существующему массиву перехватчиков, а не заменит их.

Заключение

Сервис аутентификации (AuthService) управляет входом/выходом пользователей и хранением токенов, а сервис защиты авторизации (AuthGuardService) обеспечивает безопасную навигацию, ограничивая доступ для аутентифицированных пользователей. Сервис-перехватчик (JwtInterceptor) перехватывает исходящие HTTP-запросы, чтобы включить токен JWT в заголовок Authorization. Интегрировав эти компоненты в конфигурацию маршрутов и модулей приложения, можно создать надежную систему аутентификации и авторизации, повышающую безопасность и удобство работы пользователей.

Читайте также:

Читайте нас в Telegram, VK и Дзен


Перевод статьи Faruktaiwo: User Authentication and Authorization in angular 16 with JWT

Предыдущая статьяМатематика, скрывающаяся за “проклятием размерности”
Следующая статья21 рекомендация по HTML