Как я могу охранять маршруты в Angular?

В настоящее время после входа в систему я могу получить JWT на фронтенд. В настоящее время мое приложение имеет страницу регистрации в качестве целевой страницы, и как только пользователь входит в систему, маршрут проверяет аутентификацию для перенаправления на охраняемый домашний путь

Моей первой интуицией было отправить булево значение из бэкенда (Django) и использовать его для создания guard. Но я продолжаю видеть, что, похоже, лучшей практикой является обработка этого во фронт-энде.

Я создал auth.service.ts и auth.guard.ts. В сервисе я пытаюсь получить токен из браузера, а затем проверяю, не истек ли срок его действия. Затем я вызываю этот метод в guard и возвращаю булево значение. Проблема в том, что каждый раз, когда я ищу токен в локальном хранилище, я получаю обратно null.

Есть ли лучший способ добиться этого?

auth.guard.ts

import { Injectable } from '@angular/core';
import {
  ActivatedRouteSnapshot,
  CanActivate,
  Router,
  RouterStateSnapshot,
  UrlTree,
} from '@angular/router';
import { Observable, of } from 'rxjs';
import { catchError, map } from 'rxjs/operators';
import { AuthService } from './auth.service';

@Injectable({
  providedIn: 'root',
})
export class AuthGuard implements CanActivate {
  constructor(private authService: AuthService, private router: Router) {}
  canActivate(
    route: ActivatedRouteSnapshot,
    state: RouterStateSnapshot
  ):
    | Observable<boolean | UrlTree>
    | Promise<boolean | UrlTree>
    | boolean
    | UrlTree {
    console.log(this.authService.isAuthenticated());
    if(!this.authService.isAuthenticated()){
      this.router.navigate(['/login']);
      return false;
    }
    return true;
  }
}

auth.service.ts

import { Injectable } from '@angular/core';
import { JwtHelperService } from '@auth0/angular-jwt';

@Injectable({
  providedIn: 'root'
})
export class AuthService {
  public jwtHelper: JwtHelperService = new JwtHelperService();
  constructor() { }

  isAuthenticated(){
    const jwt = localStorage.getItem('token');
    return !this.jwtHelper.isTokenExpired(jwt!);
  }
}

app-routing.module.ts

...
import { AuthGuard } from './user/services/auth.guard'

const routes: Routes = [
  {
    path: '',
    component: LandingComponent,
    children: [
      { path: '', component: HomeComponent, canActivate: [AuthGuard],},
      { path: 'home', component: HomeComponent, canActivate: [AuthGuard],},
      {
        path: 'cohort-charts',
        component: CohortChartsComponent,
        children: [
          { path: 'selection', component: CohortSelectionComponent },
          { path: 'edit', component: CohortEditComponent },
          { path: '', redirectTo: 'selection', pathMatch: 'full' },
        ],
      },
    ],
  },
  {
    path: 'login',
    component: LoginComponent,
  },
  { path: '**', redirectTo: '' },
];

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

Я предполагаю, что каждый раз, когда вы ищете токен в локальном хранилище, вы получаете обратно null, потому что вы не сохраняете токен, или если вы это делаете, вы пытаетесь сохранить токен как объект, а не сериализованный (как строку, стробируя его), поэтому он не сохраняется, или когда вы получаете его, вы не распаковываете его.

В любом случае, я полагаю, что лучшей практикой для управления всей секцией jwt/аутентификации будет перехватчик:

А Interceptor - это сервис, который перехватывает все ваши http вызовы, и вы можете установить, чтобы он делал что-то автоматически (например, магизировал jwt).

Дополнительная информация о том, как добавлять и обновлять заголовки и как использовать перехватчик для перехвата запросов и ответов:

https://angular.io/guide/http#adding-and-updating-headers

https://angular.io/guide/http#intercepting-requests-and-responses

Я оставляю вам представление о том, как это делается / что вам нужно:

  1. Обеспечьте угловой перехватчик
app.module.ts:
 providers: [
    {
      provide: HTTP_INTERCEPTORS,
      useClass: HttpJwtAuthInterceptor,
      multi: true,
    },
    { provide: BASE_PATH, useValue: environment.apiUrl },
  ],
  1. Сначала, в вашем auth.service.ts, 2 метода для хранения/получения токена
// STORE the token in localstore:
setToken(token:string){

   // First, serialize it (but just if token is not string type).
   const tokenString:string = JSON.stringify( token );
 
   localStorage.setItem('token', tokenString);
}
 
// READ the token from localstorage and Deserialize
getToken(): string | null{
 
   let token = localStorage.getItem( 'token' );
 
   if( token !=null){

       // You just need to parse if you serialized it inside setToken() method
       token = JSON.parse(carItemsString);
  }
 
  return token;
 
}
  1. Затем, в вашем перехватчике:
import { Injectable } from '@angular/core';
import {
  HttpRequest,
  HttpHandler,
  HttpEvent,
  HttpInterceptor,
} from '@angular/common/http';

import { AuthService } from '../_services/auth.service';

@Injectable()
export class AuthInterceptor implements HttpInterceptor {

  constructor(private authService: AuthService) {}

  intercept(
    request: HttpRequest<any>,
    next: HttpHandler
  ): Observable<HttpEvent<any>> {
  
  const url="\yourAPI\endpoint";
  
    //  Get your token
    cont myToken = this.authService.getToken();
     
    // Add authorization header with token if available   
 
    if (myToken) {
    
       request = request.clone({
          setHeaders: {
            Authorization: `Bearer ${currentUser.user.api_token}`,
            'Content-Type': 'application/json',
          },
          url,
        });
        
    } 
    
    …
return next.handle(request);

    }
Вернуться на верх