简体   繁体   English

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

[英]Angular2 - How to best handle expired authentication token?

I am using Angular 2.1.2. 我正在使用Angular 2.1.2。

I have an authentication token (using angular2-jwt) and if it expires my webApi call fails with a 401 error. 我有一个身份验证令牌(使用angular2-jwt),如果它到期,我的webApi调用失败,出现401错误。 I am looking for a solution where the user will not lose any input data. 我正在寻找一个解决方案,用户不会丢失任何输入数据。

I can catch this 401 and open a modal with the login. 我可以抓住这个401并用登录打开一个模态。 The user then logs in, the modal goes away, and they see their input screen. 然后用户登录,模态消失,他们看到他们的输入屏幕。 However, the failed requests are showing errors so I need to reprocess the requests. 但是,失败的请求显示错误,因此我需要重新处理请求。 If it was a router navigate the initial data has not loaded. 如果是路由器导航,则初始数据未加载。

I could reload the page, but if I router.navigate to the same page it does not seem to actually reload the page. 我可以重新加载页面,但如果我router.navigate到同一页面,它似乎并没有真正重新加载页面。 I don't want to do a full page reload on the single page app. 我不想在单页面应用上进行整页重新加载。 Is there a way to force router.navigate to run, even if it's the current page? 有没有办法强制router.navigate运行,即使它是当前页面?

Re-navigating is still a problem because I would lose any new input data that has not saved. 重新导航仍然是一个问题,因为我会丢失任何未保存的新输入数据。

Ideally, the request would just 'pause' until the user logs in from the modal. 理想情况下,请求只是“暂停”,直到用户从模态登录。 I have not found a way to implement this. 我还没有找到实现这个的方法。

Any ideas? 有任何想法吗? Is there a best practice? 有最好的做法吗?

Usually I would provide a HttpService myself instead of using Http directly. 通常我会自己提供HttpService而不是直接使用Http So with your requirement, I can provide my own get() method to chain the authentication before sending any real HTTP requests. 因此,根据您的要求,我可以提供自己的get()方法来链接身份验证,然后再发送任何真实的HTTP请求。

Here is the service: 这是服务:

@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');
      }
    });
  }
}

Here is the component to call the service: 以下是调用服务的组件:

@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);
   }
}

By chaining observables, the Authentication service determines whether to interrupt the normal flow to show the modal (though currently only lives with the App component, it can certainly be implemented separately). 通过链接observable, Authentication服务确定是否中断正常流程以显示模态(虽然目前只与App组件一起使用,但它当然可以单独实现)。 And once a positive answer is received from the dialog, the service can resume the flow. 一旦从对话框收到肯定答复,服务就可以恢复流程。

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);
    }
  }
}

I have a full live example here. 我在这里有一个完整的实例。

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

Ideally, the request would just 'pause' until the user logs in from the modal. 理想情况下,请求只是“暂停”,直到用户从模态登录。 I have not found a way to implement this. 我还没有找到实现这个的方法。

Did you try to switch your button by using tokenNotExpired attribute like in this example : https://github.com/auth0/angular2-jwt#checking-authentication-to-hideshow-elements-and-handle-routing 您是否尝试使用tokenNotExpired属性切换按钮,如下例所示: https//github.com/auth0/angular2-jwt#checking-authentication-to-hideshow-elements-and-handle-routing

It allows you to prevent the 401... 它可以让你防止401 ......

After I do a login and receive a token (that in my case expires in 60 minutes), I set a interval that checks every minute to see if 59 minutes have passed. 在我登录并收到令牌(在我的情况下在60分钟后到期)后,我设置了一个间隔,检查每分钟是否已经过了59分钟。 If yes, then I open up a login dialog. 如果是,那么我打开一个登录对话框。

The idea, however, is the login dialog is not a route, but just an overlay that opens on top of whatever screen the user happens to be in on (thus I put the login component within the html of the app root component, and use an observable to call up the login dialog from anywhere in the app). 然而,这个想法是登录对话框不是一个路径,而只是一个覆盖在用户碰巧所在的任何屏幕上打开(因此我将登录组件放在app根组件的html中,并使用一个可观察的,从应用程序的任何地方调用登录对话框)。

When the user correctly re-logins in, I close the login dialog and the user goes merrily on their way with whatever screen they were using before they had to re-login. 当用户正确地重新登录时,我关闭登录对话框,并且用户在他们必须重新登录之前使用他们正在使用的任何屏幕快乐地前进。

I don't know if this is a "best practice" but it works in the situation you are describing. 我不知道这是否是“最佳实践”,但它适用于您所描述的情况。 Let me know and I can put up code. 让我知道,我可以提出代码。

Use a browser session: 使用浏览器会话:

https://developer.mozilla.org/de/docs/Web/API/Window/sessionStorage https://developer.mozilla.org/de/docs/Web/API/Window/sessionStorage

Store the data as json string and then recreate form data if request fails. 将数据存储为json字符串,然后在请求失败时重新创建表单数据。

Well, reloading is simple: (<any>window).location.reload(true); 好吧,重新加载很简单: (<any>window).location.reload(true);

It is good idea to show login/password popup and allow user to continue working if he can provide password, if user hits cancel just do redirect to login page. 最好显示登录/密码弹出窗口并允许用户继续工作,如果他可以提供密码,如果用户点击取消只是重定向到登录页面。

There are also connection problems, timeouts, server errors. 还有连接问题,超时,服务器错误。 The problem is that it is difficult to reliably implement proper handling using Angular 2 Http module and rxjs . 问题是使用Angular 2 Http模块和rxjs很难可靠地实现正确的处理。 Personally, i gave up using Http module, because its error handling has bad design and implemented my own http module, which allows configurable error handling and transparent retries. 就个人而言,我放弃了使用Http模块,因为它的错误处理设计不好并实现了我自己的http模块,它允许可配置的错误处理和透明重试。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM