Angular: запускать canActivate при каждом изменении маршрута

Недавно я застрял с охранниками маршрута Angular. CanActive запускается только один раз при загрузке страницы и не запускается при изменении маршрута в рамках защищенного маршрута. Я думаю, что это было изменено, потому что раньше оно запускалось при каждом изменении. Из того, что я читал на форумах, я должен использовать CanActivateChild. Дело в том, что наше приложение состоит из нескольких модулей, у которых есть несколько потомков маршрута, и когда я использую CanActivateChild в корневом модуле, он вызывается несколько раз при изменении маршрута.

Я считаю глупым назначать защиту для каждого дочернего элемента, потому что для AppModule эти лениво загружаемые дочерние модули должны быть просто «черными ящиками», и я хотел определить, что все эти модули должны быть защищены.

export const routes: Routes = [
  {
    path: '404',
    component: NotFoundComponent
  },
  {
    path: '',
    canActivate: [AuthGuard],
    component: FullLayoutComponent,
    data: {
      title: 'Home'
    },
    children: [
      {
        path: 'administration',
        loadChildren: './administration/administration.module#AdministrationModule'
      },
      {
        path: 'settings',
        loadChildren: './settings/settings.module#SettingsModule'
      }
    ]
  },
  {
    path: '',
    loadChildren: './account/account.module#AccountModule'
  },
  {
    path: '**',
    redirectTo: '404'
  }
];

Есть ли какое-нибудь решение для этого? Или Вы считаете это «не проблемой» в отношении безопасности?

Спасибо вам всем.


person Filip Matys    schedule 18.10.2017    source источник


Ответы (2)


Столкнулся с той же проблемой, и все, что мне удалось найти по этой проблеме, — это несколько закрытых проблем на Github с заявлениями разработчиков Angular о том, что такое поведение «предусмотрено дизайном».

Итак, в итоге я подписался на события навигации в app.component и запустил там проверку AuthGuard:

constructor(
  private router: Router,
  private route: ActivatedRoute,
  private authGuard: AuthGuard,
) {}

ngOnInit() {
  this.router.events
    .subscribe(event => {
      if (event instanceof RoutesRecognized) {
        this.guardRoute(event);
      }
    }));
}

private guardRoute(event: RoutesRecognized): void {
  if (this.isPublic(event)) {
    return;
  }

  if (!this.callCanActivate(event, this.authGuard)) {
    return;
  }
}

private callCanActivate(event: RoutesRecognized, guard: CanActivate) {
  return guard.canActivate(this.route.snapshot, event.state);
}

private isPublic(event: RoutesRecognized) {
  return event.state.root.firstChild.data.isPublic;
}

AuthGuard довольно стандартен:

@Injectable()
export class AuthGuard implements CanActivate{

  constructor(private auth: AuthService, private router: Router) { }

  canActivate(): Promise<boolean> {
    return this.auth.isLoggedInPromise()
      .then(isLoggedIn => {
        if (!isLoggedIn) {
          this.router.navigate(["/login"]);
        }
        return isLoggedIn;
      });
    }
  }

Публичные маршруты должны быть настроены так:

{
  path: "login",
  component: LoginComponent,
  data: { isPublic: true }
}

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

Вдохновленный этим ответом.

person liri2006    schedule 18.10.2017
comment
Ага, нашел точно такой же. По дизайну. Я считаю ваш ответ лучшим решением на данный момент, так что спасибо! - person Filip Matys; 19.10.2017

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

Но Angular научился предоставлять вам способ настроить поведение охранников и распознавателей непосредственно в вашем routes.ts:

export const routes: Routes = [
  {
    path: '404',
    component: NotFoundComponent
  },
  {
    path: '',
    canActivate: [AuthGuard],
    runGuardsAndResolvers: 'always',
    children: [
       ....
    ]
  }
]

Вот документы: https://angular.io/api/router/RunGuardsAndResolvers

Есть хороший пост в блоге, объясняющий ваши варианты: https://juristr.com/blog/2019/01/Explore-Angular-Routers-runGuardsAndResolvers/

person nuclearglow    schedule 28.01.2020