Как реализовать аутентификацию через JWT?angular-89

Общая схема работы JWT в Angular

  1. Пользователь вводит credentials (логин/пароль)
  2. Сервер проверяет и возвращает JWT токен
  3. Клиент сохраняет токен и прикрепляет к последующим запросам
  4. Сервер проверяет токен и возвращает данные

1. Установка необходимых пакетов

npm install @auth0/angular-jwt

2. Настройка модуля аутентификации

import { JwtModule } from '@auth0/angular-jwt';

export function tokenGetter() {
  return localStorage.getItem('access_token');
}

@NgModule({
  imports: [
    JwtModule.forRoot({
      config: {
        tokenGetter: tokenGetter,
        allowedDomains: ['api.example.com'],
        disallowedRoutes: ['api.example.com/auth/login']
      }
    })
  ]
})
export class AuthModule {}

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

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

@Injectable({
  providedIn: 'root'
})
export class AuthService {
  private readonly loginUrl = '/api/auth/login';

  constructor(private http: HttpClient) {}

  login(credentials: {email: string, password: string}) {
    return this.http.post<{token: string}>(this.loginUrl, credentials).pipe(
      tap(res => {
        localStorage.setItem('access_token', res.token);
      })
    );
  }

  logout() {
    localStorage.removeItem('access_token');
  }

  isLoggedIn(): boolean {
    return !!localStorage.getItem('access_token');
  }

  getToken(): string | null {
    return localStorage.getItem('access_token');
  }
}

4. HTTP Interceptor для JWT

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

@Injectable()
export class JwtInterceptor implements HttpInterceptor {
  constructor(private auth: AuthService) {}

  intercept(request: HttpRequest<unknown>, next: HttpHandler) {
    const token = this.auth.getToken();

    if (token) {
      request = request.clone({
        setHeaders: {
          Authorization: `Bearer ${token}`
        }
      });
    }

    return next.handle(request);
  }
}

Не забудьте зарегистрировать интерсептор в модуле:

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

5. Защита роутов

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 auth: AuthService, private router: Router) {}

  canActivate(): boolean {
    if (this.auth.isLoggedIn()) {
      return true;
    }

    this.router.navigate(['/login']);
    return false;
  }
}

Использование в роутинге:

{
  path: 'profile',
  component: ProfileComponent,
  canActivate: [AuthGuard]
}

6. Обработка истечения токена

Добавьте в интерсептор проверку 401 ошибки:

intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
  return next.handle(request).pipe(
    catchError(err => {
      if (err.status === 401) {
        this.auth.logout();
        this.router.navigate(['/login']);
      }
      return throwError(err);
    })
  );
}

7. Обновление токена

private refreshToken() {
  return this.http.post<{token: string}>('/api/auth/refresh', {
    refreshToken: localStorage.getItem('refresh_token')
  }).pipe(
    tap(res => {
      localStorage.setItem('access_token', res.token);
    })
  );
}

// В интерсепторе:
if (err.status === 401 && !request.url.includes('/auth/refresh')) {
  return this.auth.refreshToken().pipe(
    switchMap(() => {
      request = request.clone({
        setHeaders: {
          Authorization: `Bearer ${this.auth.getToken()}`
        }
      });
      return next.handle(request);
    }),
    catchError(() => {
      this.auth.logout();
      this.router.navigate(['/login']);
      return throwError('Session expired');
    })
  );
}

8. Декодирование JWT токена

Установите пакет:

npm install jwt-decode

Использование:

import jwt_decode from 'jwt-decode';

getUserInfo(): any {
  const token = this.getToken();
  if (token) {
    return jwt_decode(token);
  }
  return null;
}

isTokenExpired(): boolean {
  const token = this.getToken();
  if (!token) return true;

  const date = this.getTokenExpirationDate(token);
  if (date === undefined) return false;
  return !(date.valueOf() > new Date().valueOf());
}

9. Лучшие практики безопасности

  1. Храните токен только в localStorage или sessionStorage
  2. Никогда не храните JWT в cookies с HttpOnly=false
  3. Всегда используйте HTTPS
  4. Установите разумное время жизни токена (15-30 минут)
  5. Реализуйте Refresh Token с меньшим временем жизни
  6. Добавьте проверку origin для токенов

Резюмируем

Реализация JWT в Angular требует создания сервиса аутентификации, HTTP интерсептора и route guards. Важно правильно обрабатывать истечение токена и соблюдать меры безопасности. Использование библиотеки @auth0/angular-jwt значительно упрощает интеграцию.