Angular 11 может активировать защиту в зависимости от результата запроса HTTP

#angular #asynchronous #routes

Вопрос:

Я хочу запретить обычным пользователям доступ /players , /scores и, если они попытаются, он должен перенаправить их на /home . Доступ к другим маршрутам разрешен только администраторам. Моя проблема в том, что данные, указывающие, является ли пользователь администратором или нет, поступают из асинхронной функции. В файле guard: this.getService.getUserRoles() возвращает роли пользователя(администратор-это роль).

     // to check wether user is admin or not
   async isAdmin(): Promise<boolean> {
     if (JSON.stringify(await this.getService.getUserRoles()).includes("admin")) {
      return true;
    }
    return false;
  }

  canActivate(
    route: ActivatedRouteSnapshot,
    state: RouterStateSnapshot) {
    const path: string = route.paramMap.get('path');
    if (!(this.isAdmin())) {
      if (path === "players" || path === "scores") {
        // router: Router
        this.router.navigate(['/home']);
        return false;
      }
    }
    return true;
  }
 

Я добавил canActivate:[RoutesGuard] оба маршрута в модуль маршрутизации приложений.
Есть идеи, что случилось?

Комментарии:

1. можете ли вы напечатать путь в console.log

Ответ №1:

Я использую этого охранника

 import { Injectable } from '@angular/core';
import { HttpErrorResponse } from '@angular/common/http';
import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
import { AdvancedRouter } from '@mintplayer/ng-router';
import { AccountService } from '../../services/account/account.service';

@Injectable({
  providedIn: 'root'
})
export class IsInRoleGuard implements CanActivate {
  constructor(
    private accountService: AccountService,
    private router: AdvancedRouter,
  ) {
  }

  canActivate(next: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
    return this.accountService.currentRoles().then((roles) => {
      let routeRoles = <string[]>next.data.roles;
      let intersect = roles.filter(r => routeRoles.indexOf(r) > -1);
      return intersect.length > 0;
    }).catch((error: HttpErrorResponse) => {
      this.router.navigate(['/account', 'login'], {
        queryParams: {
          return: state.url
        }
      });
      return false;
    });
  }
  
}
 

Это модуль маршрутизации, в котором я внедряю охрану:

 const routes: Routes = [
  { path: '', loadChildren: () => import('./list/list.module').then(m => m.ListModule) },
  { path: ':id/:name', loadChildren: () => import('./show/show.module').then(m => m.ShowModule) },
  { path: 'create', loadChildren: () => import('./create/create.module').then(m => m.CreateModule), canActivate: [IsInRoleGuard], canDeactivate: [HasChangesGuard], data: { roles: ['Blogger'] } },
  { path: ':id/:name/edit', loadChildren: () => import('./edit/edit.module').then(m => m.EditModule), canActivate: [IsInRoleGuard], canDeactivate: [HasChangesGuard], data: { roles: ['Blogger'] } },
];

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

Вы можете буквально просто вернуть Promise<boolean> из своего canActivate метода.

Ответ №2:

Вы должны вернуть наблюдаемое из canActivate guard, иначе Angular не будет ждать завершения вызова API и выполнения других операций.

 canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> | boolean {
    return this.getService.getUserRoles()
      .pipe(
        map(resp => {              
          if (allowed) return true;
          else {
            // return false and navigate to home
            this.router.navigateByUrl(`home`);
            return false;
          }
        })
      );
  }