Как заставить 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
        }
    }));
  }
}
Другие вопросы по тегам