【问题标题】:Angular: Run canActivate on each route changeAngular:在每次路由更改时运行 canActivate
【发布时间】:2018-03-30 00:41:45
【问题描述】:

我最近被 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'
  }
];

有什么解决办法吗?还是您认为这对于安全性“不是问题”?

谢谢大家。

【问题讨论】:

    标签: angular router canactivate


    【解决方案1】:

    订阅路由器事件的问题是导航已经开始并且历史已经更新,这使得很难像警卫那样以可靠的方式阻止导航。

    但是 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/

    【讨论】:

      【解决方案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 }
      }
      

      这种实现的好处是默认情况下所有内容都受到保护,并且应该明确配置公共路由,这将减少某些路由不受保护的可能性。还将将此重构为某种服务,以便能够在多个应用程序中使用它。

      灵感来自this answer

      【讨论】:

      • 是的,我发现完全一样。 “按设计”。我发现你的答案是迄今为止最好的解决方案,所以谢谢!
      猜你喜欢
      • 2017-02-02
      • 1970-01-01
      • 1970-01-01
      • 2016-08-21
      • 1970-01-01
      • 1970-01-01
      • 2018-03-18
      • 1970-01-01
      相关资源
      最近更新 更多