Как заставить AuthGuard ждать LocalStorage в Angular 4?
Я разработал веб-сайт на Angular 4 и ASP.Net Core. Однако есть одна вещь, которая меня беспокоит. Он имеет некоторую базовую реализацию входа в систему с JWT, и я использую локальное хранилище для сохранения объекта пользователя и токена.
Все работает отлично, когда пользователь входит в систему, но всякий раз, когда я обновляю браузер, Angular всегда просит меня войти снова. У меня есть метод в AppComponent, чтобы обновлять токен и возвращать новый объект пользователя каждый раз, когда приложение запускается (логика происходит в ядре asp net, если он может повторно проверить токен, сохраненный в localStorage или нет).
Во всяком случае, я искал вокруг, и я считаю, что мне нужно вернуть Observables. Вот еще одна тема с очень похожей проблемой:
AuthGuard не ждет завершения аутентификации перед проверкой пользователя
Основное различие заключается в том, что в ответах используется огненная база, и я считаю, что их объект уже является наблюдаемым, а мой - логическим.
Вот мои коды:
StorageService (localStorage)
export class StorageService {
constructor(private storage: LocalStorageService) { }
public user: User = null;
public get isLoggedIn() : boolean {
return this.user != null;
}
public isInRole(roleName: string): boolean {
if (!this.isLoggedIn)
return false;
for (let r of this.user.roles) {
if (r.name == roleName)
return true;
}
return false;
}
public get token(): string {
return <string>this.storage.get(STORAGE_TOKEN) || null;
}
public set token(value: string) {
if (value == null) {
this.storage.remove(STORAGE_TOKEN);
}
else {
this.storage.set(STORAGE_TOKEN, value);
}
}
}
Реализация AuthGuard
export class IsLoggedIn implements CanActivate {
constructor(private storage: StorageService, private router: Router) { }
canActivate(childRoute: ActivatedRouteSnapshot, state: RouterStateSnapshot){
if (this.storage.isLoggedIn) {
return true;
}
this.router.navigate(['account/login'], {
queryParams: {
return: state.url
}
});
return false;
}
}
Обновление токена в AppComponent (ngOnInit)
this.net.get<LoginModel>(`Authentication/RefreshToken`).subscribe(t => {
this.storage.token = t.token;
this.storage.user = t.user;
});
Наконец, вот как я использую IsInRole:
export class AdminOnly implements CanActivate {
constructor(
private storage: StorageService,
private router: Router
) { }
canActivate(
childRoute: ActivatedRouteSnapshot,
state: RouterStateSnapshot
) {
var message: string = null;
if (!this.storage.isLoggedIn) {
message = 'Você precisa se logar!';
}
if (!this.storage.isInRole('Admin')) {
message = 'Você precisa ser um administrador!';
}
if (message == null) {
return true;
}
this.router.navigate(['account/login'], {
queryParams: {
return: state.url,
message: message
}
});
return false;
}
}
Как мне преобразовать мои логические переменные в Observables? Любое другое мнение о том, как решить эту проблему, приветствуется. Заранее спасибо.
1 ответ
Вы можете использовать .of, который находится на наблюдаемом объекте, как статическую функцию. Всякий раз, когда вы подписываетесь, он будет ссылаться на текущее состояние объекта.
Rx.Observable.of(state)
Вот пример codepen: https://codepen.io/ndcunningham/pen/OvVxjx?editors=1111
Сервис LocalStorage
public get isLoggedIn() : Observable<boolean> {
return Rx.Observable.of(this.user !== null);
}
охрана
export class IsLoggedIn implements CanActivate {
constructor(private storage: StorageService, private router: Router) { }
canActivate(childRoute: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
return this.storage.isLoggedIn.do(e => !e && this.router.navigate(['account/login'], {
queryParams: {
return: state.url
}
}));
}
}