pathmatch navigate child login typescript angular angular2-routing

login - child - router.navigate angular 5



Redireccionamiento angular a la página de inicio de sesión (7)

¡Por favor, no anule la salida del enrutador! Es una pesadilla con la última versión del enrutador (3.0 beta).

En su lugar, use las interfaces CanActivate y CanDeactivate y configure la clase como canActivate / canDeactivate en su definición de ruta.

Como eso:

{ path: '''', component: Component, canActivate: [AuthGuard] },

Clase:

@Injectable() export class AuthGuard implements CanActivate { constructor(protected router: Router, protected authService: AuthService) { } canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> | boolean { if (state.url !== ''/login'' && !this.authService.isAuthenticated()) { this.router.navigate([''/login'']); return false; } return true; } }

Ver también: https://angular.io/docs/ts/latest/guide/router.html#!#can-activate-guard

Vengo del mundo Asp.Net MVC donde los usuarios que intentan acceder a una página que no están autorizados son redirigidos automáticamente a la página de inicio de sesión.

Estoy tratando de reproducir este comportamiento en Angular. Me encontré con el decorador @CanActivate, pero resulta en que el componente no se procesa en absoluto, no hay redirección.

Mi pregunta es la siguiente:

  • ¿Angular proporciona una forma de lograr este comportamiento?
  • ¿Si es así, cómo? ¿Es una buena práctica?
  • Si no, ¿cuál sería la mejor práctica para manejar la autorización del usuario en Angular?

Aquí hay un ejemplo actualizado usando Angular 4

Rutas con ruta de inicio protegida por AuthGuard

import { Routes, RouterModule } from ''@angular/router''; import { LoginComponent } from ''./login/index''; import { HomeComponent } from ''./home/index''; import { AuthGuard } from ''./_guards/index''; const appRoutes: Routes = [ { path: ''login'', component: LoginComponent }, // home route protected by auth guard { path: '''', component: HomeComponent, canActivate: [AuthGuard] }, // otherwise redirect to home { path: ''**'', redirectTo: '''' } ]; export const routing = RouterModule.forRoot(appRoutes);

AuthGuard redirige a la página de inicio de sesión si el usuario no ha iniciado sesión

Actualizado para pasar la URL original en los parámetros de consulta a la página de inicio de sesión

import { Injectable } from ''@angular/core''; import { Router, CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot } from ''@angular/router''; @Injectable() export class AuthGuard implements CanActivate { constructor(private router: Router) { } canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) { if (localStorage.getItem(''currentUser'')) { // logged in so return true return true; } // not logged in so redirect to login page with the return url this.router.navigate([''/login''], { queryParams: { returnUrl: state.url }}); return false; } }

Para ver el ejemplo completo y la demostración de trabajo, puede consultar esta publicación


Consulte este código, archivo auth.ts

import { CanActivate } from ''@angular/router''; import { Injectable } from ''@angular/core''; import { } from ''angular-2-local-storage''; import { Router } from ''@angular/router''; @Injectable() export class AuthGuard implements CanActivate { constructor(public localStorageService:LocalStorageService, private router: Router){} canActivate() { // Imaginary method that is supposed to validate an auth token // and return a boolean var logInStatus = this.localStorageService.get(''logInStatus''); if(logInStatus == 1){ console.log(''****** log in status 1*****'') return true; }else{ console.log(''****** log in status not 1 *****'') this.router.navigate([''/'']); return false; } } } // *****And the app.routes.ts file is as follow ******// import { Routes } from ''@angular/router''; import { HomePageComponent } from ''./home-page/home- page.component''; import { WatchComponent } from ''./watch/watch.component''; import { TeachersPageComponent } from ''./teachers-page/teachers-page.component''; import { UserDashboardComponent } from ''./user-dashboard/user- dashboard.component''; import { FormOneComponent } from ''./form-one/form-one.component''; import { FormTwoComponent } from ''./form-two/form-two.component''; import { AuthGuard } from ''./authguard''; import { LoginDetailsComponent } from ''./login-details/login-details.component''; import { TransactionResolver } from ''./trans.resolver'' export const routes:Routes = [ { path:'''', component:HomePageComponent }, { path:''watch'', component:WatchComponent }, { path:''teachers'', component:TeachersPageComponent }, { path:''dashboard'', component:UserDashboardComponent, canActivate: [AuthGuard], resolve: { dashboardData:TransactionResolver } }, { path:''formone'', component:FormOneComponent, canActivate: [AuthGuard], resolve: { dashboardData:TransactionResolver } }, { path:''formtwo'', component:FormTwoComponent, canActivate: [AuthGuard], resolve: { dashboardData:TransactionResolver } }, { path:''login-details'', component:LoginDetailsComponent, canActivate: [AuthGuard] }, ];


Siguiendo las impresionantes respuestas anteriores, también me gustaría CanActivateChild : proteger las rutas secundarias. Se puede usar para agregar guard a las rutas de los niños útiles para casos como ACL

Dice así

src / app / auth-guard.service.ts (extracto)

import { Injectable } from ''@angular/core''; import { CanActivate, Router, ActivatedRouteSnapshot, RouterStateSnapshot, CanActivateChild } from ''@angular/router''; import { AuthService } from ''./auth.service''; @Injectable() export class AuthGuard implements CanActivate, CanActivateChild { constructor(private authService: AuthService, private router: Router) {} canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean { let url: string = state.url; return this.checkLogin(url); } canActivateChild(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean { return this.canActivate(route, state); } /* . . . */ }

src / app / admin / admin-routing.module.ts (extracto)

const adminRoutes: Routes = [ { path: ''admin'', component: AdminComponent, canActivate: [AuthGuard], children: [ { path: '''', canActivateChild: [AuthGuard], children: [ { path: ''crises'', component: ManageCrisesComponent }, { path: ''heroes'', component: ManageHeroesComponent }, { path: '''', component: AdminDashboardComponent } ] } ] } ]; @NgModule({ imports: [ RouterModule.forChild(adminRoutes) ], exports: [ RouterModule ] }) export class AdminRoutingModule {}

Esto está tomado de https://angular.io/docs/ts/latest/guide/router.html#!#can-activate-guard


1. Create a guard as seen below. 2. Install ngx-cookie-service to get cookies returned by external SSO. 3. Create ssoPath in environment.ts (SSO Login redirection). 4. Get the state.url and use encodeURIComponent.

import { Injectable } from ''@angular/core''; import { CanActivate, Router, ActivatedRouteSnapshot, RouterStateSnapshot } from ''@angular/router''; import { CookieService } from ''ngx-cookie-service''; import { environment } from ''../../../environments/environment.prod''; @Injectable() export class AuthGuardService implements CanActivate { private returnUrl: string; constructor(private _router: Router, private cookie: CookieService) {} canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean { if (this.cookie.get(''MasterSignOn'')) { return true; } else { let uri = window.location.origin + ''/#'' + state.url; this.returnUrl = encodeURIComponent(uri); window.location.href = environment.ssoPath + this.returnUrl ; return false; } } }


Actualización: publiqué un proyecto de esqueleto completo de Angular 2 con integración OAuth2 en Github que muestra la directiva mencionada a continuación en acción.

Una forma de hacerlo sería mediante el uso de una directive . A diferencia de los components Angular 2, que son básicamente etiquetas HTML nuevas (con código asociado) que inserta en su página, una directiva atributiva es un atributo que coloca en una etiqueta que provoca un comportamiento. Documentos aquí .

La presencia de su atributo personalizado hace que sucedan cosas en el componente (o elemento HTML) en el que colocó la directiva. Considere esta directiva que uso para mi aplicación Angular2 / OAuth2 actual:

import {Directive, OnDestroy} from ''angular2/core''; import {AuthService} from ''../services/auth.service''; import {ROUTER_DIRECTIVES, Router, Location} from "angular2/router"; @Directive({ selector: ''[protected]'' }) export class ProtectedDirective implements OnDestroy { private sub:any = null; constructor(private authService:AuthService, private router:Router, private location:Location) { if (!authService.isAuthenticated()) { this.location.replaceState(''/''); // clears browser history so they can''t navigate with back button this.router.navigate([''PublicPage'']); } this.sub = this.authService.subscribe((val) => { if (!val.authenticated) { this.location.replaceState(''/''); // clears browser history so they can''t navigate with back button this.router.navigate([''LoggedoutPage'']); // tells them they''ve been logged out (somehow) } }); } ngOnDestroy() { if (this.sub != null) { this.sub.unsubscribe(); } } }

Esto hace uso de un servicio de autenticación que escribí para determinar si el usuario ya ha iniciado sesión y también se suscribe al evento de autenticación para que pueda expulsar a un usuario si se desconecta o se desconecta.

Podrías hacer lo mismo. Crearía una directiva como la mía que verifique la presencia de una cookie necesaria u otra información de estado que indique que el usuario está autenticado. Si no tienen esos indicadores que está buscando, redirija al usuario a su página pública principal (como yo) o su servidor OAuth2 (o lo que sea). Pondría ese atributo de directiva en cualquier componente que necesite protección. En este caso, podría llamarse protected como en la directiva que pegué anteriormente.

<members-only-info [protected]></members-only-info>

Luego, querrá navegar / redirigir al usuario a una vista de inicio de sesión dentro de su aplicación y manejar la autenticación allí. Tendría que cambiar la ruta actual a la que deseaba hacer. Entonces, en ese caso, usaría la inyección de dependencia para obtener un objeto Router en la función constructor() su directiva y luego usaría el método de navigate() para enviar al usuario a su página de inicio de sesión (como en mi ejemplo anterior).

Esto supone que tiene una serie de rutas en algún lugar que controlan una etiqueta <router-outlet> que se parece a esto, tal vez:

@RouteConfig([ {path: ''/loggedout'', name: ''LoggedoutPage'', component: LoggedoutPageComponent, useAsDefault: true}, {path: ''/public'', name: ''PublicPage'', component: PublicPageComponent}, {path: ''/protected'', name: ''ProtectedPage'', component: ProtectedPageComponent} ])

Si, en cambio, necesita redirigir al usuario a una URL externa , como su servidor OAuth2, entonces su directiva debería hacer algo como lo siguiente:

window.location.href="https://myserver.com/oauth2/authorize?redirect_uri=http://myAppServer.com/myAngular2App/callback&response_type=code&client_id=clientId&scope=my_scope


Uso con el enrutador final

Con la introducción del nuevo enrutador se hizo más fácil proteger las rutas. Debe definir un guardia, que actúa como un servicio, y agregarlo a la ruta.

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

Ahora pase LoggedInGuard a la ruta y también agréguelo a la matriz de providers del módulo.

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

La declaración del módulo:

@NgModule({ declarations: [AppComponent, HomeComponent, LoginComponent] imports: [HttpModule, BrowserModule, RouterModule.forRoot(routes)], providers: [UserService, LoggedInGuard], bootstrap: [AppComponent] }) class AppModule {}

Publicación de blog detallada sobre cómo funciona con la versión final: https://medium.com/@blacksonic86/angular-2-authentication-revisited-611bf7373bf9

Uso con el enrutador obsoleto

Una solución más sólida es extender el RouterOutlet y, al activar una ruta, verificar si el usuario ha iniciado sesión. De esta manera, no tiene que copiar y pegar su directiva en cada componente. Además, la redirección basada en un subcomponente puede ser engañosa.

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

UserService representa el lugar donde reside la lógica de su negocio, ya sea que el usuario haya iniciado sesión o no. Puede agregarlo fácilmente con DI en el constructor.

Cuando el usuario navega a una nueva url en su sitio web, se llama al método de activación con la Instrucción actual. Desde allí puedes tomar la URL y decidir si está permitido o no. Si no, simplemente redirige a la página de inicio de sesión.

Lo último que queda por hacer para que funcione, es pasarlo a nuestro componente principal en lugar del integrado.

@Component({ selector: ''app'', directives: [LoggedInRouterOutlet], template: template }) @RouteConfig(...) export class AppComponent { }

Esta solución no se puede usar con el decorador de ciclo de vida @CanActive , porque si la función que se le pasa se resuelve falsa, no se llamará al método de RouterOutlet del RouterOutlet .

También escribió una publicación de blog detallada al respecto: https://medium.com/@blacksonic86/authentication-in-angular-2-958052c64492