Angular2 - Как лучше всего использовать истекший токен аутентификации?

Я использую Angular 2.1.2.

У меня есть токен аутентификации (с использованием angular2 -jwt), и, если он истекает, мой вызов webApi завершается с ошибкой 401. Я ищу решение, в котором пользователь не потеряет никаких входных данных.

Я могу поймать этот 401 и открыть модаль с логином. Затем пользователь входит в систему, модаль уходит, и они видят экран ввода. Однако неудавшиеся запросы показывают ошибки, поэтому мне нужно перепрограммировать запросы. Если это был переход к маршрутизатору, исходные данные не были загружены.

Я могу перезагрузить страницу, но если я роутер.навигаюсь на той же странице, она, похоже, не перезагружает страницу. Я не хочу делать полную перезагрузку страницы в приложении с одной страницей. Есть ли способ заставить router.navigate работать, даже если это текущая страница?

Повторная навигация по-прежнему является проблемой, потому что я потеряю любые новые входные данные, которые не были сохранены.

В идеале запрос будет просто "приостанавливаться", пока пользователь не войдет в систему из модального. Я не нашел способ реализовать это.

Любые идеи? Есть ли наилучшая практика?

Ответы

Ответ 1

Обычно я предлагаю HttpService сам, а не напрямую Http. Поэтому с вашим требованием я могу предоставить свой собственный метод get(), чтобы связать аутентификацию перед отправкой любых реальных HTTP-запросов.

Вот сервис:

@Injectable()
class HttpService {
  constructor(private http: Http, private auth: Authentication) {}

  public get(url: string): Observable<Response> {
    return this.auth.authenticate().flatMap(authenticated => {
      if (authenticated) {
        return this.http.get(url);
      }
      else {
        return Observable.throw('Unable to re-authenticate');
      }
    });
  }
}

Вот компонент для вызова службы:

@Component({
  selector: 'my-app',
  template: `<h1>Hello {{name}}</h1>
  <button (click)="doSomething()">Do Something</button>

  <div [hidden]="!auth.showModal">
  <p>Do you confirm to log in?</p>
  <button (click)="yes()">Yes</button><button (click)="no()">No</button>
  </div>
  `,
})
export class AppComponent {
   name = 'Angular';

   constructor(private httpSvc: HttpService, public auth: Authentication) {}

   ngOnInit() {
   }

   doSomething() {
     let a = this.httpSvc.get('hello.json').subscribe(() => {
       alert('Data retrieved!');
     }, err => {
       alert(err);
     });
   }

   yes() {
    this.auth.confirm.emit(true);
   }

   no() {
     this.auth.confirm.emit(false);
   }
}

По цепочке наблюдаемых служба Authentication определяет, следует ли прерывать нормальный поток, чтобы показать модальный (хотя в настоящее время он живет только с компонентом App, он может быть реализован отдельно). И как только положительный ответ получен из диалога, служба может возобновить поток.

class Authentication {
  public needsAuthentication = true;
  public showModal = false;
  public confirm = new EventEmitter<boolean>();

  public authenticate(): Observable<boolean> {
    // do something to make sure authentication token works correctly
    if (this.needsAuthentication) {
      this.showModal = true;
      return Observable.create(observer => {
        this.confirm.subscribe(r => {
          this.showModal = false;
          this.needsAuthentication = !r; 
          observer.next(r);
          observer.complete();
        });
      });
    }
    else {
      return Observable.of(true);
    }
  }
}

У меня есть полный живой пример здесь.

http://plnkr.co/edit/C129guNJvri5hbGZGsHp?open=app%2Fapp.component.ts&p=preview

Ответ 2

В идеале запрос будет просто "приостанавливаться", пока пользователь не войдет в систему из модального. Я не нашел способ реализовать это.

Вы пытались переключить свою кнопку, используя атрибут tokenNotExpired, как в этом примере: https://github.com/auth0/angular2-jwt#checking-authentication-to-hideshow-elements-and-handle-routing

Это позволяет вам предотвратить 401...

Ответ 4

Ну, перезагрузка проста: (<any>window).location.reload(true);

Хорошая идея показать всплывающее окно входа/пароля и разрешить пользователю продолжать работу, если он может предоставить пароль, если пользовательский клик отменяет, просто перенаправляйте страницу входа.

Существуют также проблемы с подключением, тайм-ауты, ошибки сервера. Проблема в том, что трудно надежно реализовать надлежащую обработку с помощью модуля Angular 2 Http и rxjs. Лично я отказался от использования модуля Http, потому что его обработка ошибок имела плохую конструкцию и реализовала мой собственный модуль http, который позволяет настраивать обработку ошибок и прозрачные попытки.

Ответ 5

После того, как я сделаю логин и получаю токен (который в моем случае истекает через 60 минут), я устанавливаю интервал, который проверяет каждую минуту, чтобы проверить, прошло ли 59 минут. Если да, то я открываю диалоговое окно входа в систему.

Идея, однако, заключается в том, что диалоговое окно входа в систему - это не маршрут, а просто оверлей, который открывается поверх любого экрана, на котором находится пользователь (таким образом, я поместил компонент входа в html корневого компонента приложения, и используйте наблюдаемое для вызова диалогового окна входа в систему из любого места приложения).

Когда пользователь правильно заново войдет в систему, я закрою диалоговое окно входа в систему, и пользователь весело идет по пути с каким-либо экраном, который они использовали, прежде чем им пришлось повторно войти в систему.

Я не знаю, является ли это "лучшей практикой", но она работает в ситуации, которую вы описываете. Дайте мне знать, и я могу поместить код.