web-dev-qa-db-fra.com

Rediriger l'utilisateur avec le routeur en fonction de l'état de connexion

Je souhaite me connecter automatiquement à une page de connexion si l'utilisateur n'est pas connecté.

app.module.ts

import { RouterModule, Routes } from '@angular/router';
import { AppComponent } from './app.component';
import { LoginComponent } from './login/login.component';
import { DashBoardComponent} from './dashboard/dashboard.component';
import { NotFoundComponent } from './not-found/not-found.component';

const APPROUTES: Routes = [
  {path: 'home', component: AppComponent},
  {path: 'login', component: LoginComponent},
  {path: 'dashboard', component: DashboardComponent},
  {path: '**', component: NotFoundComponent}
];

@NgModule({
  declarations: [
    AppComponent,
    LoginComponent,
    DashboardComponent
    NotFoundComponent
  ],
  imports: [
    BrowserModule,
    FormsModule,
    HttpModule,
    MaterialModule.forRoot(),
    RouterModule.forRoot(APPROUTES)
  ],
  providers: [],
  bootstrap: [AppComponent]
})

Si l'utilisateur n'est pas connecté, le LoginComponent devrait se charger, sinon le DashboardComponent.

30
Rafael Moura

Voici 3 façons de faire ce que vous avez demandé, du moins préféré au préféré:

Option 1. Rediriger impérativement l'utilisateur dans AppComponent

@Component({
  selector: 'app-root',
  template: `...`
})
export class AppComponent {
  constructor(authService: AuthService, router: Router) {
    if (authService.isLoggedIn()) {
      router.navigate(['dashboard']);
    }
  }
}

Pas très bien. Il est préférable de conserver les informations de "connexion requise" dans la déclaration de route à laquelle elles appartiennent.

Option 2. Utilisez un CanActivate guard

Ajoutez un garde CanActivate à tous les itinéraires qui nécessitent la connexion de l'utilisateur:

const APPROUTES: Routes = [
  {path: 'home', component: AppComponent, canActivate:[LoginActivate]},
  {path: 'dashboard', component: DashBoardComponent, canActivate:[LoginActivate]},
  {path: 'login', component: LoginComponent},
  {path: '**', component: NotFoundComponent}
];

Ma garde s'appelle LoginActivate.

Pour que cela fonctionne, je dois ajouter la protection au providers de mon module.

Et puis je dois le mettre en œuvre. Dans cet exemple, je vais utiliser la protection pour rediriger l'utilisateur s'il n'est pas connecté:

@Injectable()
export class LoginActivate implements CanActivate {
  constructor(private authService: AuthService, private router: Router) {}

  canActivate(
    route: ActivatedRouteSnapshot,
    state: RouterStateSnapshot
  ): Observable<boolean>|Promise<boolean>|boolean {
    if (!this.authService.isLoggedIn()) {
      this.router.navigate(['login']);
    }
    return true;
  }
}

Consultez la documentation sur les gardes de route si cela n’a aucun sens: https://angular.io/docs/ts/latest/guide/router.html#guards

Cette option est meilleure mais pas super flexible. Que se passe-t-il si nous devons rechercher d'autres conditions que "connecté" telles que les autorisations des utilisateurs? Et si nous devions passer un paramètre à la garde, comme le nom d'un rôle "admin", "editor" ...?

Option 3. Utilisez la propriété route data

La meilleure solution à mon humble avis est d’ajouter des métadonnées dans la déclaration des routes pour indiquer "cette route nécessite que l’utilisateur soit connecté".

Nous pouvons utiliser la propriété route data pour cela. Il peut contenir des données arbitraires et dans ce cas, j’ai choisi d’inclure un drapeau requiresLogin qui est soit true, soit false (false sera la valeur par défaut si n'est pas défini):

const APPROUTES: Routes = [
  {path: 'home', component: AppComponent, data:{requiresLogin: true}},
  {path: 'dashboard', component: DashBoardComponent, data:{requiresLogin: true}}
];

Maintenant, la propriété data en elle-même ne fait rien. Mais je peux l'utiliser pour appliquer ma logique de "connexion requise". Pour cela, j'ai encore besoin d'un CanActivate garde.

Dommage, vous dites. Maintenant, je dois ajouter 2 choses à chaque route protégée: les métadonnées ET la garde ...

MAIS:

  • Vous pouvez associer le CanActivate garde à un itinéraire de niveau supérieur et il sera exécuté pour tous ses itinéraires enfants [À CONFIRMER]. De cette façon, vous ne devez utiliser la garde qu'une seule fois. Bien sûr, cela ne fonctionne que si les itinéraires à protéger sont tous des enfants d'un itinéraire parent (ce n'est pas le cas dans l'exemple de Rafael Moura).
  • La propriété data nous permet de transmettre toutes sortes de paramètres à la garde, par exemple. le nom d'un rôle spécifique ou de l'autorisation de vérification, un nombre de points ou de crédits que l'utilisateur doit posséder pour accéder à la page, etc.

Compte tenu de ces remarques, il est préférable de renommer la garde en quelque chose de plus générique, comme AccessGuard.

Je montrerai seulement le morceau de code où le garde récupère le data attaché à la route, car ce que vous faites à l'intérieur du garde dépend vraiment de votre situation:

@Injectable()
export class AccessGuard implements CanActivate {
  canActivate(route: ActivatedRouteSnapshot): Observable<boolean>|Promise<boolean>|boolean {
    const requiresLogin = route.data.requiresLogin || false;
    if (requiresLogin) {
      // Check that the user is logged in...
    }
  }
}

Pour que le code ci-dessus soit exécuté, vous devez suivre un itinéraire similaire à:

{
  path: 'home',
  component: AppComponent,
  data: { requiresLogin: true },
  canActivate: [ AccessGuard ]
}

NB N'oubliez pas d'ajouter AccessGuard à votre providers de votre module.

73
AngularChef

Vous pouvez aussi faire quelque chose comme ça:

{
  path: 'home',
  component: getHomeComponent(),
  data: { requiresLogin: true },
  canActivate: [ AccessGuard ]
}

Puis:

export function getHomeComponent(): Type<Component> {
  if (User.isLoggedIn) {
    return <Type<Component>>HomeComponent;
  }
  else{
    return <Type<Component>>LoginComponent;
  }
}
2
Christian Patzer