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

时间:2016-11-04 17:04:20

标签: angular angular2-routing angular2-jwt

我正在使用Angular 2.1.2。

我有一个身份验证令牌(使用angular2-jwt),如果它过期,我的webApi调用将失败并显示401错误。我正在寻找一种解决方案,用户不会丢失任何输入数据。

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

我可以重新加载页面,但如果我router.navigate到同一页面,它似乎并没有真正重新加载页面。我不想在单页面应用上进行整页重新加载。有没有办法强制router.navigate运行,即使它是当前页面?

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

理想情况下,请求只会“暂停”,直到用户从模式登录。我还没有找到实现这个目标的方法。

有什么想法吗? 有最好的做法吗?

5 个答案:

答案 0 :(得分: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);
   }
}

通过链接observable,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

答案 1 :(得分:3)

  

理想情况下,请求只会“暂停”,直到用户从模式登录。我还没有找到实现这个目标的方法。

您是否尝试使用此示例中的tokenNotExpired属性切换按钮:https://github.com/auth0/angular2-jwt#checking-authentication-to-hideshow-elements-and-handle-routing

它允许你阻止401 ...

答案 2 :(得分:3)

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

然而,这个想法是登录对话框不是路由,而只是在用户碰巧所在的任何屏幕上打开的叠加层(因此我将登录组件放在app根组件的html中) ,并使用observable从应用程序的任何位置调用登录对话框)。

当用户正确地重新登录时,我关闭登录对话框,用户在他们必须重新登录之前使用他们使用的任何屏幕快乐地前进。

我不知道这是不是最好的做法&#34;但它适用于您描述的情况。让我知道,我可以提出代码。

答案 3 :(得分:2)

使用浏览器会话:

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

将数据存储为json字符串,然后在请求失败时重新创建表单数据。

答案 4 :(得分:2)

嗯,重新加载很简单:(<any>window).location.reload(true);

最好显示登录/密码弹出窗口并允许用户继续工作,如果他可以提供密码,如果用户点击取消,只需重定向到登录页面。

还存在连接问题,超时,服务器错误。问题是使用Angular 2 Http模块和rxjs很难可靠地实现正确的处理。就个人而言,我放弃使用Http模块,因为它的错误处理设计不好并实现了我自己的http模块,它允许可配置的错误处理和透明重试。