Как я могу охранять маршруты в 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
Я оставляю вам представление о том, как это делается / что вам нужно:
- Обеспечьте угловой перехватчик
app.module.ts:
providers: [
{
provide: HTTP_INTERCEPTORS,
useClass: HttpJwtAuthInterceptor,
multi: true,
},
{ provide: BASE_PATH, useValue: environment.apiUrl },
],
- Сначала, в вашем 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;
}
- Затем, в вашем перехватчике:
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);
}