C'est un peu difficile d'être concret, mais je vais essayer de vous donner quelques conseils sur la façon de gérer l'authentification dans angular.
Certains outils livrés avec Angular sur lesquels je me concentre sur ce sujet sont : APP_INITIALIZER ( un article sur ce sujet - car les documents sont rares), HttpInterceptor , LoadingComponent ou simplement le composant d'application typique.
Certaines dépendances qui m'aident également dans ce processus sont les suivantes ngx-store et magasin ngxs . Malgré des noms similaires, ce sont des outils différents.
Je ne vais pas vous donner une réponse complète à votre problème, mais quelques conseils :
Dans un service d'authentification, vous pouvez, par exemple, enregistrer un callback pour écouter la modification d'un certain cookie (grâce à ngx -store). Quelque chose comme ceci :
constructor(public cookiesStorageService: CookiesStorageService,
@Inject(JWT_COOKIE_NAME) private _JWT_COOKIE_NAME: string) {
this.cookiesStorageService
.observe(this._JWT_COOKIE_NAME)
.subscribe((cookie: NgxStorageEvent) => this.checkIfNewToken(cookie.newValue));
}
Remarquez que ci-dessus, on injecte le nom du cookie pour le jeton JWT. Je trouve que c'est plus clair et que cela colle aux principes d'Angular :
export const JWT_COOKIE_NAME = new InjectionToken<string>('ACTUAL_JWT_COOKIE_NAME');
Dans le cas ci-dessus, si vous transmettez un jeton JWT via des cookies (pas d'en-tête d'authentification). Si vous passez un en-tête portant le jeton, vous pourriez faire quelque chose comme intercepter les requêtes HTTP. Quelque chose comme :
@Injectable()
export class AuthenticationInterceptor implements HttpInterceptor {
constructor(private tokenExtractor: HttpXsrfTokenExtractor,
private authService: AuthenticationService,
@Inject(API_ENDPOINT) private _API_ENDPOINT: string) {
}
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
if (req.url.match(this._API_ENDPOINT)) {
// this.authService.intercept$.next(req);
const XSRFTokenHeaderName = 'X-XSRF-TOKEN';
const XSRFToken = this.tokenExtractor.getToken() as string;
if (XSRFToken !== null && !req.headers.has(XSRFTokenHeaderName)) {
req = req.clone({headers: req.headers.set(XSRFTokenHeaderName, XSRFToken)});
}
req = req.clone();
return next.handle(req);
} else {
return next.handle(req).map(event => {
if (event instanceof HttpResponse) {
// do something with response like sending it to an authentication service
}
return event;
});
}
}
}
Je laisse l'exemple canonique du traitement de X-XSRF-TOKEN.
Un initialisateur d'application pourrait faire quelque chose comme distribuer une action de connexion - ou appeler directement une méthode de service d'authentification (j'aime utiliser ngxs store pour ce genre de choses) :
export function appRun(store: Store) {
return () =>
store
.dispatch(new Login())
.pipe(finalize(() => true)) // let the app handle errors after bootstrapped
.toPromise();
}
Et dans un composant de chargement ou le composant d'application ont quelque chose comme ceci :
constructor(
private router: Router,
private actions$: Actions
) {}
ngOnInit() {
this.actions$
.pipe(ofActionErrored(Login))
.subscribe(() => this.router.navigate([Routes.PUBLIC]));
this.actions$
.pipe(ofActionSuccessful(Logout))
.subscribe(() => this.router.navigate([Routes.PUBLIC]));
}
NGXS est livré avec des gestionnaires utiles de succès d'action ou d'erreur d'action que vous pouvez utiliser pour acheminer quelque part (les routes ci-dessus sont définies dans un Enum).
Je laisse donc de nombreuses étapes en dehors de cette réponse (comme la déclaration des états, l'enregistrement de l'APP_INITIALIZER, de l'Interceptor, ...) mais n'hésitez pas si vous pensez que cela peut aider à commenter pour plus d'informations. Les bibliothèques mentionnées sont très puissantes et peuvent vous aider à résoudre votre problème de différentes manières (ou pourraient finir par n'être qu'une surcharge - un service pour stocker certains états et un intercepteur pourraient suffire). Ce n'est pas très concret, mais je pense que c'est un bon ensemble de conseils pour vous aider à démarrer.
edit : J'ai oublié les gardiens de route. Elles peuvent aussi aider à l'authentification dans angular. Les gardes CanLoad (pour les modules chargés paresseusement) et CanActivate en particulier. Quelque chose comme :
canActivateRead(): Observable<boolean> | boolean {
const perm = this.store.selectSnapshot(state => state.module.acl);
if (perm) {
return this.canRead(perm);
} else {
return this.fetchACLAndTestPermission('READ');
}
}
private fetchACLAndTestPermission(perm: 'READ' | 'CREATE' | 'UPDATE'): Observable<boolean> {
return this.authService.getPermissionForACL('ACL').pipe(
tap(permission => this.store.dispatch(new SetMainACL({ permission }))),
map(perm => this.canRead(perm)),
tap(isPermitted => (isPermitted ? isPermitted : this.feedback.notAllowed()))
);
}
Et vous pouvez hériter dans un service de garde :
@Injectable({
providedIn: 'root'
})
export class ParameterBaseGuard extends ParameterGuards implements CanLoad {
constructor(public authService: AuthenticationService, public feedback: FeedbackService, public store: Store) {
super(authService, feedback, store);
}
canLoad(): Observable<boolean> | Promise<boolean> | boolean {
return this.fetchACLAndTestPermission('READ');
}
canActivate(): Observable<boolean> | Promise<boolean> | boolean {
return this.canActivateRead();
}
}