简体   繁体   中英

Angular: Run canActivate on each route change

I got stuck recently with Angular route guards. CanActive runs only once when the page is loaded and does not run on route change within the guarded route. I think this was changed because it used to run on each change. From what I read in forums, I should use CanActivateChild. The thing is, our application consists of several modules, that have several route descendants and when I use CanActivateChild in root module, it is called several times when changing the route.

I find it dumb to assign a guard to each child because, for AppModule, those lazy loaded child modules should be just 'Black Boxes' and I wanted to define that all those modules should be guarded.

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'
  }
];

Is there any solution to this? Or do You find this as 'not an issue' regarding security?

Thank You all.

Faced the same issue and all I was able to find on issue are few closed issues on Github with Angular devs statements that such behavior "is by design".

So what I ended up doing is subscribing on navigation events in app.component and firing AuthGuard check there:

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 is rather standard:

@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;
      });
    }
  }

And public routes should be configured like this:

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

The plus of such implementation is that everything is protected by default and public route should be configured explicitly, which will reduce the possibility of leaving some routes unprotected. Will also refactor this into some kind of service, to be able to use it across multiple apps.

Inspired by this answer .

The issue with subscribing to router events is that the navigation has already been started and the history has been updated, which makes it hard to prevent the navigation in a reliable way as a guard does.

But Angular has learned to provide you with a way to configure how guards and resolvers should behave directly in your routes.ts :

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

Here's the docs: https://angular.io/api/router/RunGuardsAndResolvers

There's a nice blogpost explaining your options: https://juristr.com/blog/2019/01/Explore-Angular-Routers-runGuardsAndResolvers/

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM