Usage with the final router
With the introduction of the new router it became easier to guard the routes. You must define a guard, which acts as a service, and add it to the route.
import { Injectable } from '@angular/core';
import { CanActivate } from '@angular/router';
import { UserService } from '../../auth';
@Injectable()
export class LoggedInGuard implements CanActivate {
constructor(user: UserService) {
this._user = user;
}
canActivate() {
return this._user.isLoggedIn();
}
}
Now pass the LoggedInGuard
to the route and also add it to the providers
array of the module.
import { LoginComponent } from './components/login.component';
import { HomeComponent } from './components/home.component';
import { LoggedInGuard } from './guards/loggedin.guard';
const routes = [
{ path: '', component: HomeComponent, canActivate: [LoggedInGuard] },
{ path: 'login', component: LoginComponent },
];
The module declaration:
@NgModule({
declarations: [AppComponent, HomeComponent, LoginComponent]
imports: [HttpModule, BrowserModule, RouterModule.forRoot(routes)],
providers: [UserService, LoggedInGuard],
bootstrap: [AppComponent]
})
class AppModule {}
Detailed blog post about how it works with the final release: https://medium.com/@blacksonic86/angular-2-authentication-revisited-611bf7373bf9
Usage with the deprecated router
A more robust solution is to extend the RouterOutlet
and when activating a route check if the user is logged in. This way you don't have to copy and paste your directive to every component. Plus redirecting based on a subcomponent can be misleading.
@Directive({
selector: 'router-outlet'
})
export class LoggedInRouterOutlet extends RouterOutlet {
publicRoutes: Array;
private parentRouter: Router;
private userService: UserService;
constructor(
_elementRef: ElementRef, _loader: DynamicComponentLoader,
_parentRouter: Router, @Attribute('name') nameAttr: string,
userService: UserService
) {
super(_elementRef, _loader, _parentRouter, nameAttr);
this.parentRouter = _parentRouter;
this.userService = userService;
this.publicRoutes = [
'', 'login', 'signup'
];
}
activate(instruction: ComponentInstruction) {
if (this._canActivate(instruction.urlPath)) {
return super.activate(instruction);
}
this.parentRouter.navigate(['Login']);
}
_canActivate(url) {
return this.publicRoutes.indexOf(url) !== -1 || this.userService.isLoggedIn()
}
}
The UserService
stands for the place where your business logic resides whether the user is logged in or not. You can add it easily with DI in the constructor.
When the user navigates to a new url on your website, the activate method is called with the current Instruction. From it you can grab the url and decide whether it is allowed or not. If not just redirect to the login page.
One last thing remain to make it work, is to pass it to our main component instead of the built in one.
@Component({
selector: 'app',
directives: [LoggedInRouterOutlet],
template: template
})
@RouteConfig(...)
export class AppComponent { }
This solution can not be used with the @CanActive
lifecycle decorator, because if the function passed to it resolves false, the activate method of the RouterOutlet
won't be called.
Also wrote a detailed blog post about it: https://medium.com/@blacksonic86/authentication-in-angular-2-958052c64492