В этой статье разберемся в тонкостях аутентификации и авторизации пользователей в 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. Интегрировав эти компоненты в конфигурацию маршрутов и модулей приложения, можно создать надежную систему аутентификации и авторизации, повышающую безопасность и удобство работы пользователей.
Читайте также:
- Angular и Wiz: вместе лучше
- Создание computedAsync для вычисления значений сигналов в Angular
- Современный подход к разработке Angular
Читайте нас в Telegram, VK и Дзен
Перевод статьи Faruktaiwo: User Authentication and Authorization in angular 16 with JWT