Аутентификация и авторизация

Аутентификация и авторизация являются ключевыми аспектами безопасности современных веб-приложений. В Angular их реализация требует понимания маршрутизации, сервисов, интерсепторов HTTP и работы с токенами.

Аутентификация

Аутентификация — процесс подтверждения личности пользователя. Наиболее распространённый способ аутентификации в Angular — использование JSON Web Token (JWT).

Пример сервиса аутентификации:

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable, BehaviorSubject } from 'rxjs';
import { tap } from 'rxjs/operators';

@Injectable({
  providedIn: 'root'
})
export class AuthService {
  private authUrl = 'https://api.example.com/auth';
  private currentUserSubject = new BehaviorSubject<string | null>(null);

  constructor(private http: HttpClient) {}

  login(username: string, password: string): Observable<any> {
    return this.http.post<any>(`${this.authUrl}/login`, { username, password })
      .pipe(tap(response => {
        localStorage.setItem('token', response.token);
        this.currentUserSubject.next(username);
      }));
  }

  logout(): void {
    localStorage.removeItem('token');
    this.currentUserSubject.next(null);
  }

  get token(): string | null {
    return localStorage.getItem('token');
  }

  get currentUser(): Observable<string | null> {
    return this.currentUserSubject.asObservable();
  }
}

Ключевые моменты:

  • Хранение токена в localStorage или sessionStorage.
  • Использование BehaviorSubject для отслеживания текущего состояния пользователя.
  • Метод login получает токен и сохраняет его для дальнейшего использования.

Авторизация

Авторизация определяет права доступа пользователя к ресурсам приложения. В Angular она реализуется с помощью Guard-ов.

Пример Guard-а для маршрутов:

import { Injectable } from '@angular/core';
import { CanActivate, 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(): boolean {
    if (this.authService.token) {
      return true;
    } else {
      this.router.navigate(['/login']);
      return false;
    }
  }
}

Основные принципы:

  • Проверка наличия токена для разрешения доступа.
  • Перенаправление на страницу входа при отсутствии аутентификации.
  • Использование Guard-а в маршрутах:
{
  path: 'dashboard',
  component: DashboardComponent,
  canActivate: [AuthGuard]
}

Интерсепторы HTTP

Для автоматической передачи токена в заголовках HTTP-запросов используется интерсептор.

Пример интерсептора:

import { Injectable } from '@angular/core';
import { HttpInterceptor, HttpRequest, HttpHandler, HttpEvent } from '@angular/common/http';
import { Observable } from 'rxjs';
import { AuthService } from './auth.service';

@Injectable()
export class TokenInterceptor implements HttpInterceptor {
  constructor(private authService: AuthService) {}

  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    const token = this.authService.token;
    if (token) {
      const cloned = req.clone({
        headers: req.headers.set('Authorization', `Bearer ${token}`)
      });
      return next.handle(cloned);
    }
    return next.handle(req);
  }
}

Регистрация интерсептора в модуле приложения:

import { HTTP_INTERCEPTORS } from '@angular/common/http';

providers: [
  { provide: HTTP_INTERCEPTORS, useClass: TokenInterceptor, multi: true }
]

Роли и уровни доступа

Для реализации сложной авторизации удобно хранить роли пользователя в токене или отдельном сервисе. Пример использования Guard-а с ролями:

canActivate(): boolean {
  const userRole = this.authService.getUserRole();
  if (userRole === 'admin') {
    return true;
  } else {
    this.router.navigate(['/not-authorized']);
    return false;
  }
}

Практика:

  • Хранить минимально необходимую информацию в токене.
  • Проверять права на уровне интерфейса и на уровне серверного API.
  • Обновлять токен по истечении срока действия через refresh-токен.

Защита маршрутов и компонентов

Кроме маршрутов, авторизация может применяться к отдельным компонентам с помощью директив:

<ng-container *ngIf="authService.hasRole('admin')">
  <button>Удалить пользователя</button>
</ng-container>

Обработка ошибок аутентификации

Необходимо централизованно обрабатывать ошибки, связанные с истекшими токенами или несанкционированным доступом:

import { catchError } from 'rxjs/operators';
import { throwError } from 'rxjs';

this.http.get('/protected-resource')
  .pipe(
    catchError(err => {
      if (err.status === 401) {
        this.authService.logout();
        this.router.navigate(['/login']);
      }
      return throwError(() => err);
    })
  );

Ключевые принципы:

  • Централизованная обработка HTTP-ошибок.
  • Автоматический редирект на страницу входа при 401.
  • Предотвращение доступа к защищённым ресурсам без токена.

Итоговая структура

Для надежной аутентификации и авторизации в Angular рекомендуется строить архитектуру на основе следующих элементов:

  • AuthService — управление токеном и состоянием пользователя.
  • Guards — контроль доступа к маршрутам.
  • HTTP Interceptors — автоматическое добавление токена в запросы.
  • Компонентная авторизация — управление видимостью UI-элементов на основе ролей.
  • Централизованная обработка ошибок — безопасная работа с истекшими токенами и отказами доступа.

Эта схема обеспечивает модульность, масштабируемость и безопасное управление доступом в приложениях на Angular.