Angular2 - 如何最好地处理过期的身份验证令牌?

16

我正在使用Angular 2.1.2。

我有一个鉴权令牌(使用angular2-jwt),如果过期,我的webApi调用将失败并出现401错误。我正在寻找一种解决方案,使用户不会失去任何输入数据。

我可以捕获到这个401错误并打开一个带有登录的模态框。然后用户登录,模态框消失,他们可以看到他们的输入屏幕。然而,失败的请求仍然显示错误,因此我需要重新处理这些请求。如果是路由器导航,则最初的数据未加载。

我可以重新加载页面,但如果我通过router.navigate导航到同一页,则似乎实际上不会重新加载该页面。我不想对单页应用程序进行完整页面重新加载。是否有一种方法可以强制router.navigate运行,即使它是当前页面?

重新导航仍然是一个问题,因为我会丢失尚未保存的任何新输入数据。

理想情况下,请求将暂停,直到用户从模态框中登录。我还没有找到实现这一点的方法。

有什么思路吗?是否有最佳做法?


使用Redux处理应用程序状态怎么样? - Andrea Alhena
1
如果您绑定到共享服务的数据,则在重新导航后不会丢失。类似于Redux建议。 - Günter Zöchbauer
页面重新加载后是否需要保存? - maxime1992
5个回答

5
通常我会自己提供一个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

3

3

在我登录并收到一个令牌(在我的情况下,60分钟后过期)之后,我设置了一个间隔,每分钟检查是否已经过去了59分钟。如果是,则打开一个登录对话框。

然而,这个想法是登录对话框不是路由,而只是一个覆盖在用户当前所在屏幕上方的叠加层(因此我将登录组件放在应用程序根组件的html中,并使用可观察对象从应用程序的任何位置调用登录对话框)。

当用户正确重新登录时,我关闭登录对话框,用户可以愉快地继续使用他们在重新登录之前使用的任何屏幕。

我不知道这是否是“最佳实践”,但它适用于您描述的情况。让我知道,我可以放出代码。


2

2

重新加载很简单:(<任何>window).location.reload(true);

向用户展示登录/密码弹窗并允许其继续工作,如果用户提供密码,则这是个好主意,如果用户点击取消,则重定向到登录页面。

还有连接问题、超时、服务器错误等。问题在于使用 Angular 2 Http 模块和 rxjs 可靠实现适当的处理非常困难。就我个人而言,我放弃了使用 Http 模块,因为它的错误处理设计不佳,并自己实现了一个 http 模块,它允许可配置的错误处理和透明的重试。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接