Redirection angulaire vers la page de connexion

je viens de la Asp.Net MVC world où les utilisateurs qui tentent d'accéder à une page qu'ils ne sont pas autorisés sont automatiquement redirigés vers la page de connexion.

j'essaie de reproduire ce comportement en angle. Je suis venu à travers le décorateur @CanActivate, mais il en résulte que le composant ne rend pas du tout, pas de redirection.

ma question Est la suivante:

  • Ne Angulaire de fournir un moyen d'atteindre cet le comportement?
  • Si oui, comment? Est-ce une bonne pratique?
  • dans la négative, quelle serait la meilleure pratique pour traiter L'autorisation de l'utilisateur en angle?
97
demandé sur ishandutta2007 2015-12-17 12:52:12

7 réponses

mise à Jour: j'ai publié un squelette entier Angulaire 2 projet avec OAuth2 intégration sur Github qui montre la directive mentionnée ci-dessous dans l'action.

une façon de le faire serait d'utiliser un directive . Contrairement à Angular 2 components , qui sont essentiellement de nouvelles balises HTML (avec le code associé) que vous insérez dans votre page, une directive attributive est un attribut que vous mettez dans une balise qui provoque un certain comportement à se produire. Docs ici .

la présence de votre attribut personnalisé fait que des choses arrivent au composant (ou à L'élément HTML) dans lequel vous avez placé la directive. Considérez cette directive que j'utilise pour mon application actuelle Angular2 / OAuth2:

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

utilise un service D'authentification que j'ai écrit pour déterminer si l'utilisateur est déjà connecté ou non et souscrit également à l'événement d'authentification afin qu'il puisse virer un utilisateur s'il se déconnecte ou s'il se déconnecte.

vous pourriez faire la même chose. Vous pouvez ainsi créer une directive comme la mienne qui vérifie la présence de cookies ou d'autres informations d'état qui indique que l'utilisateur est authentifié. S'ils n'ont pas les options que vous recherchez, redirigez l'utilisateur vers votre page publique principale (comme je le fais) ou votre serveur OAuth2 (ou autre). Vous mettez que attribut de directive sur tout élément qui doit être protégé. Dans ce cas, on pourrait l'appeler protected comme dans la directive que j'ai collée ci-dessus.

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

alors vous voudriez naviguer/rediriger l'utilisateur vers une vue de connexion dans votre application, et gérer l'authentification là. Tu devrais changer la route actuelle pour celle que tu voulais faire. Dans ce cas, vous utiliseriez l'injection de dépendances pour obtenir un objet Routeur dans votre la fonction constructor() de la directive et ensuite utiliser la méthode navigate() pour envoyer l'utilisateur à votre page de connexion (comme dans mon exemple ci-dessus).

cela suppose que vous avez une série de routes quelque part contrôlant une étiquette <router-outlet> qui ressemble à quelque chose comme ceci, peut-être:

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

si, à la place, vous aviez besoin de rediriger l'utilisateur vers une URL externe , comme votre serveur OAuth2, alors vous auriez votre directive do quelque chose comme ce qui suit:

window.location.href="https://myserver.com/oauth2/authorize?redirect_uri=http://myAppServer.com/myAngular2App/callback&response_type=code&client_id=clientId&scope=my_scope
80
répondu Michael Oryl 2015-12-31 03:40:03

voici un exemple mis à jour utilisant L'angle 4

Routes avec route d'origine protégée par 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 vers la page de connexion si l'utilisateur n'est pas connecté dans

mise à Jour pour passer d'origine de l'url dans la requête de paramètres à la page de connexion

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

pour l'exemple complet et le travail démo vous pouvez consulter cet article

88
répondu Jason 2017-05-05 22:11:40

utilisation avec le routeur final

Avec l'introduction du nouveau routeur, il est devenu plus facile pour garder les routes. Vous devez définir un garde, qui agit comme un service, et l'ajouter à l'itinéraire.

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

passe maintenant le LoggedInGuard à la route et l'ajoute également au tableau providers du 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 },
];

la déclaration du module:

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

blog détaillé sur la façon dont il fonctionne avec la version finale: https://medium.com/@blacksonic86/angular-2-authentication-revisited-611bf7373bf9

utilisation avec le routeur déprécié

Une solution plus robuste consiste à étendre le RouterOutlet et lors de l'activation d'un itinéraire de vérifier si l'utilisateur est connecté. De cette façon, vous n'avez pas à copier et coller votre directive sur chaque composant. Majoré la redirection basée sur un sous-composant peut être trompeuse.

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

le UserService signifie l'endroit où se trouve votre logique commerciale, que l'utilisateur soit connecté ou non. Vous pouvez ajouter facilement avec DI dans le constructeur.

lorsque l'utilisateur navigue vers une nouvelle url sur votre site web, la méthode activate est appelée avec L'Instruction courante. De là, vous pouvez saisir l'url et décider si elle est autorisée ou non. Si ce n'est pas simplement rediriger vers la page de connexion.

Une dernière chose reste à le faire fonctionner, est à transmettre à notre principal composant au lieu de construit dans.

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

Cette solution ne peut pas être utilisée avec le décorateur de cycle de vie @CanActive , parce que si la fonction passe à false, la méthode activate du RouterOutlet ne sera pas appelée.

a également écrit un billet de blog détaillé à ce sujet: https://medium.com/@blacksonic86/authentication-in-angular-2-958052c64492

54
répondu Blacksonic 2016-09-28 10:38:34

s'il vous plaît, n'annulez pas la sortie du routeur! C'est un cauchemar avec la dernière version de routeur (3.0 beta).

utilisez plutôt les interfaces CanActivate et CanDeactivate et définissez la classe comme canActivate / canDeactivate dans votre définition de route.

comme ça:

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

Classe:

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

voir aussi: https://angular.io/docs/ts/latest/guide/router.html#!#peut-activer-garde

48
répondu Nilz11 2016-07-14 09:04:58

suite aux réponses géniales ci-dessus, je voudrais aussi CanActivateChild : garder les routes des enfants. Il peut être utilisé pour ajouter guard aux itinéraires pour enfants utiles pour les cas comme ACLs

ça se passe comme ça

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

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 (extrait)

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 {}

ceci est tiré de https://angular.io/docs/ts/latest/guide/router.html#!#peut-activer-garde

3
répondu Thabung 2017-03-10 12:41:35

référez ce code, auth.fichier 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]    },

]; 
2
répondu sojan 2017-05-20 11:35:23

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;      
    }
  }
}
0
répondu M.Laida 2018-06-28 05:42:11