Сложно быть конкретным, но я постараюсь дать вам несколько советов о том, как обрабатывать аутентификацию в Angular.
Некоторые инструменты, поставляемые с Angular, которые я сосредоточил на этой теме: APP_INITIALIZER ( статья на нем - потому что документы скудны), HttpInterceptor , LoadingComponent или просто типичный AppComponent.
Некоторые зависимости, которые также помогаютЯ в этом процессе ngx-store и ngxs store .Несмотря на сходные названия, они являются разными инструментами.
Я не собираюсь давать вам полный ответ на вашу проблему, но некоторые подсказки:
В службе аутентификации вы можете, например, зарегистрироватьсяобратный вызов для прослушивания изменения определенного файла cookie (благодаря ngx -store).Примерно так:
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));
}
Обратите внимание, что выше вводится имя файла cookie для токена JWT.Я нахожу это более понятным и придерживаюсь угловых принципов:
export const JWT_COOKIE_NAME = new InjectionToken<string>('ACTUAL_JWT_COOKIE_NAME');
В случае выше, если вы передаете токен JWT через куки (не заголовок аутентификации).Если вы передадите заголовок с токеном, вы можете сделать что-то вроде перехвата HTTP-запросов.Что-то вроде:
@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;
});
}
}
}
Я оставляю канонический пример обработки X-XSRF-TOKEN.
Инициализатор приложения может сделать что-то вроде отправки действия входа в систему - или фактически вызвать напрямую службу аутентификации.метод (мне нравится использовать хранилище ngxs для такого рода вещей):
export function appRun(store: Store) {
return () =>
store
.dispatch(new Login())
.pipe(finalize(() => true)) // let the app handle errors after bootstrapped
.toPromise();
}
И в компоненте загрузки или компоненте приложения есть что-то вроде этого:
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 поставляется сполезные обработчики успеха или действия с ошибкой, которые можно использовать для маршрутизации куда-либо (вышеприведенные маршруты определены в Enum).
Так что я оставляю много шагов из этого ответа (например, объявление состояний, регистрация APP_INITIALIZER), Перехватчик, ...) но не стесняйтесь, если вы полагаете, что это помогает комментировать для получения дополнительной информации.Упомянутые библиотеки очень мощные и могут помочь вам решить вашу проблему по-разному (или могут оказаться просто накладными расходами - просто служба для хранения некоторого состояния и перехватчика может быть достаточно).Это не очень конкретно, но я думаю, что это хороший набор советов, которые помогут вам.
edit: я забыл про охранников маршрута.Они также могут помочь с аутентификацией в угловых.CanLoad (для лениво загруженных модулей) и, в частности, CanActivate.Что-то вроде:
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()))
);
}
И вы можете унаследовать в службу охраны:
@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();
}
}