Progressant lentement mais sûrement avec Angular2. Et maintenant, j'ai relevé le défi suivant. Je veux vérifier si l'utilisateur est connecté ou non à chaque changement de page (en d'autres termes, en charge de chaque composant). Bien sûr, je peux implémenter l’interface OnInit dans chacun d’eux, mais c’est une odeur de code.
Existe-t-il un moyen efficace d'exécuter tout ce qui est nécessaire sur chaque page de l'application? J'aimerais entendre d'autres suggestions sur les meilleures pratiques pour gérer cette tâche.
J'utilise cette bibliothèque ( https://auth0.com/blog/2015/11/10/introducing-angular2-jwt-a-library-for-angular2-authentication/ ) pour la connexion avec jwt et j'ai déjà avoir une classe de service Nice qui encapsule toutes les fonctionnalités liées à l’authentification. Ainsi, la vérification réelle où l'utilisateur est connecté ou non est déjà faite et testée.
Merci,
Si vous utilisez le routage (et cela semble être le cas puisque vous dites: "à chaque changement de page"), vous pouvez tirer parti de plusieurs choses:
Créez un routeur-prise personnalisé (une sous-classe de RouterOutlet
) qui vérifie l'authentification avec sa méthode activate
est appelée. Dans ce cas, vous pouvez avoir quelque chose de global. Quelque chose comme ca:
@Directive({
selector: 'auth-outlet'
})
export class AuthOutlet extends RouterOutlet {
(...)
activate(oldInstruction: ComponentInstruction) {
var url = this.parentRouter.lastNavigationAttempt;
if (isAuthenticated()) {
return super.activate(oldInstruction);
} else {
(...)
}
}
}
Voir cette question pour plus de détails:
Tirez parti du décorateur CanActivate
pour vérifier si un composant peut être activé ou non. Dans votre cas, vous pouvez exécuter la vérification d'authentification à ce niveau.
Vous pouvez également faire quelque chose au niveau RouterLink pour afficher/masquer les liens de route. Dans ce cas, vous pouvez appliquer des rôles sur ces liens en fonction de la configuration de l'itinéraire et des indications pour l'utilisateur actuel. Voir cette question pour plus de détails:
Cela peut également être géré dans un intercepteur HTTP (une classe qui étend la variable Http
). Dans ce cas, lorsqu'une demande est en cours d'exécution, vous pouvez connecter quelques contrôles d'authentification:
@Injectable()
export class CustomHttp extends Http {
constructor(backend: ConnectionBackend, defaultOptions: RequestOptions) {
super(backend, defaultOptions);
}
request(url: string | Request, options?: RequestOptionsArgs): Observable<Response> {
console.log('request...');
if (isAuthenticated()) {
return super.request(url, options).catch(res => {
// do something
});
} else {
// Redirect to login page
// Or throw an exception: return Observable.throw(new Error(...));
}
}
(...)
}
Voir cette question pour plus de détails:
Je vous montre une implémentation simple avec Angular2. Vous pouvez tirer parti du hook @CanActivate comme indiqué pour vérifier si l'utilisateur est connecté ou non à la fonction isLoggedIn qui renvoie promise.
NOTE: l'implémentation ci-dessous doit vérifier si l'utilisateur est loggedIn
avant d'accéder à un composant ou non. J'espère que par quelques modifications vous pourrez réaliser ce que vous voulez avoir.
Auth.ts
import {Observable} from 'rxjs/Observable';
export class Auth {
constructor() {
this.loggedIn = false;
}
login() {
this.loggedIn = true;
}
logout() {
this.loggedIn = false;
}
check() {
return Observable.of(this.loggedIn);
}
}
isLoggedIn.ts
import {Injector} from 'angular2/core';
import {appInjector} from './appInjector';
import {Auth} from './Auth';
import {Router, ComponentInstruction} from 'angular2/router';
export const isLoggedIn = (next: ComponentInstruction, previous: ComponentInstruction) => {
let injector: Injector = appInjector(); // get the stored reference to the injector
let auth: Auth = injector.get(Auth);
let router: Router = injector.get(Router);
// return a boolean or a promise that resolves a boolean
return new Promise((resolve) => {
auth.check()
.subscribe((result) => {
if (result) {
resolve(true);
} else {
router.navigate(['/Login']);
resolve(false);
}
});
});
};
appInjector.ts
import {Injector} from 'angular2/core';
let appInjectorRef: Injector;
export const appInjector = (injector?: Injector):Injector => {
if (injector) {
appInjectorRef = injector;
}
return appInjectorRef;
};
somecomponent.ts
import {Component, View,ViewChild} from 'angular2/core';
import {CanActivate} from 'angular2/router';
import {isLoggedIn} from './isLoggedIn';
@Component({
selector: 'some',
template: 'some text'
})
@CanActivate((next: ComponentInstruction, previous: ComponentInstruction) => {
return isLoggedIn(next, previous); // this will tell whether user is loggedIn or not.
})
export class Protected {
}
boot.ts
.
.
import { provide, ComponentRef } from 'angular2/core';
import { appInjector } from './app-injector';
.
.
bootstrap(AppComponent, [...]).then((appRef: ComponentRef) => {
// store a reference to the application injector
appInjector(appRef.injector);
});
Il existe deux manières de restreindre l'accès Custom Router Outlet
et CanActivate Decorator
présentés et mis en œuvre dans cet article formidable Authentification in Angular 2
Je pense que l’extension de RouterOutlet est un moyen courant d’atteindre cet objectif.
Exemple posté il y a quelque temps dans Gitter par CaptainCodeman (pas encore testé)
import {Directive, Injector, Attribute, ElementRef, DynamicComponentLoader} from 'angular2/core';
import {Router, RouteData, RouterOutlet, RouteParams, Instruction, ComponentInstruction} from 'angular2/router';
/*
Example implementation
Given a route:
@RouteConfig([
{ path: '/thing/:id', component: ThingComponent, name: 'Thing', data: { public: false, roles:['thinger'] } }
])
authorize(instruction: ComponentInstruction):boolean {
// simplest case - route is public
if (<boolean>instruction.routeData.data['public']) {
return true;
}
// if not public then we at least need an authenticated user
if (this.isAuthenticated()) {
var routeRoles = <any[]>instruction.routeData.data['roles'];
var userRoles = <string[]>this.roles();
// no roles required for route = user just needs to be authenticated
var authorized = routeRoles.length === 0 || routeRoles.some(routeRole => userRoles.indexOf(routeRole) >= 0);
return authorized;
}
return false;
}
*/
export abstract class IAuthService {
abstract isAuthenticated():boolean;
authorize(instruction: ComponentInstruction, params:any):boolean {
// authorized if route allows public access or user is authenticated
return this.isAuthenticated() || <boolean>instruction.routeData.data['public']
}
}
@Directive({selector: 'secure-outlet'})
export class SecureRouterOutlet extends RouterOutlet {
signin:string;
unauthorized:string;
injector:Injector;
private parentRouter: Router;
private authService: IAuthService;
constructor(_elementRef: ElementRef, _loader: DynamicComponentLoader,
_parentRouter: Router, @Attribute('name') nameAttr: string,
authService:IAuthService,
injector:Injector,
@Attribute('signin') signinAttr: string,
@Attribute('unauthorized') unauthorizedAttr: string) {
super(_elementRef, _loader, _parentRouter, nameAttr);
this.parentRouter = _parentRouter;
this.authService = authService;
this.injector = injector;
this.signin = signinAttr;
this.unauthorized = unauthorizedAttr;
}
activate(nextInstruction: ComponentInstruction): Promise<any> {
var params = this.getAllRouteParams(this.injector);
var isAuthorized = this.authService.authorize(nextInstruction, params);
if (isAuthorized) {
return super.activate(nextInstruction);
}
if (this.authService.isAuthenticated()) {
var ins = this.parentRouter.generate([this.unauthorized]);
return super.activate(ins.component);
} else {
var ins = this.parentRouter.generate([this.signin,{url:location.pathname}]);
return super.activate(ins.component);
}
}
reuse(nextInstruction: ComponentInstruction): Promise<any> {
return super.reuse(nextInstruction);
}
getAllRouteParams(injector) {
let params = null;
while(injector) {
const routeParams = injector.getOptional(RouteParams);
if (routeParams) {
if (params === null) {
params = {};
} else {
params = Object.create(params);
}
Object.assign(params, routeParams.params);
}
injector = injector.parent;
}
return params;
}
}