Thursday, December 8, 2016

Angular 2 : Router Guards

The guards are used to protect routes and to check if the navigation is permitted. Sometimes, these are called during the construction of router state or after that. It is used for security and authorization purposes. There are four types of guards -

  • canLoad/ canActivate - Checks if the router can be activated
  • canActivateChild - Similar to canActivate, except that it is called when a child of the route is activated, and not the route itself.
  • canDeactivate - This is different from all others and used for confirmation not permissions. Let's say you want to show confirmation message to user if user try to close the form with unsaved changes.
I am going to discuss in detail about canActivate. canActivate guard decides if route can be activated and it gets executed when navigating to that specific route. canLoad is called during the construction of the Router State and canActivate is called after that. That's the reason canActivate constructor takes router state also as an input but canLoad only takes Route as an input. You can define multiple guards for a route and those are called in the order they are defined in the RouterConfig file.

Here is the simple use case on how to use routing guards. Suppose, you have an application where both admin and normal user can login. There is some content which only admins can see and some which is specific to normal users. But both admin and user need to authenticate first to view any content.  I created one guard to check if the user is authenticated and then created two separate guards for user and admins extending the authentication guard.


Define the routes in the Application Routing file and use the canActivate property to specify the Guard class name.

 export const APP_ROUTES: Routes = [  
   { path: 'admin', component: AdminNavComponent, children: [...ADMIN_ROUTES] ,  
     canActivate: [CanActivateViaAdminGuard]},  
   { path: 'usr', component: UserNavComponent, children: [...USER_ROUTES] ,   
     canActivate: [CanActivateViaUserGuard]},  
 ];  
 export const ROUTING = RouterModule.forRoot(APP_ROUTES);  

Where CanActivateViaAdminGuard and CanActivateViaUserGuard are defined like this -

CanActivateViaAdminGuard

 @Injectable()  
 export class CanActivateViaAdminGuard extends CanActivateViaAuthGuard {  
  constructor(public authService : AuthService, public router: Router) {  
    super(authService, router);  
  }  
  canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> 
| boolean{  
    //Call CanActivateViaAuthGuard to check if authenticated  
    if(!super.canActivate(route,state)){  
      return false;  
    }  
    else{  
      //Check the user type through authService  
      if(!(this.authService.loggedInUser.type == "admin")){  
       this.router.navigate(['403-not-authorized']);  
       return false;  
      }  
      else{  
        return true;  
      }  
    }  
  }  
 }  

CanActivateViaUserGuard
 @Injectable()  
 export class CanActivateViaUserGuard extends CanActivateViaAuthGuard {  
  constructor(public authService : AuthService, public router: Router) {  
    super(authService, router);  
  }  
  canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> 
| boolean{  
    if(!super.canActivate(route,state)){  
      return false;  
    }  
    else{  
      if(!(this.authService.loggedInUser.type == "default")){  
       this.router.navigate(['403-not-authorized']);  
       return false;  
      }  
      else{  
        return true;  
      }  
    }  
  }  
 }  

CanActivateViaAuthGuard

 @Injectable()  
 export class CanActivateViaAuthGuard implements CanActivate {  
  constructor(public authService : AuthService, public router: Router) {}  
  canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> | boolean{  
   //Check if user logged in and current url is not login page  
   if (state.url !== '/auth/signin' && !this.authService.isLoggedIn()) {  
       this.router.navigate(['/auth/signin']);  
       return false;  
   }  
   return true;  
  }  
 }  

This is not the only way to implement it. You can also define multiple guards for a route like this -

{ path: 'admin', component: AdminNavComponent, children: [...ADMIN_ROUTES] ,  
     canActivate: [CanActivateViaAuthGuard, CanActivateViaAdminGuard]} 
}

Specify, providers for the guards in the app module file -
 @NgModule({  
 //...  
 providers: [CanActivateViaAdminGuard, CanActivateViaAuthGuard, CanActivateViaUserGuard ]  
 //...  
 })

export class AppModule {

}  

Finally, in the boot.ts file
 platformBrowserDynamic().bootstrapModule(AppModule);  


No comments:

Post a Comment