【问题标题】:Angular2 - How to best handle expired authentication token?Angular2 - 如何最好地处理过期的身份验证令牌?
【发布时间】:2017-03-18 14:42:26
【问题描述】:

我正在使用 Angular 2.1.2。

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

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

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

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

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

有什么想法吗? 有最佳做法吗?

【问题讨论】:

  • 用 Redux 处理应用程序状态怎么样?
  • 如果绑定到共享服务的数据,重新导航后不会丢失。类似于 Redux 的建议。
  • 重新加载页面也需要保存吗?

标签: angular angular2-routing angular2-jwt


【解决方案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 服务通过链接 observables 来确定是否中断正常流程以显示模态(虽然目前只与 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】:

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

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

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

    我不知道这是否是“最佳做法”,但它适用于您所描述的情况。让我知道,我可以提供代码。

    【讨论】:

      【解决方案3】:

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

      您是否尝试过使用 tokenNotExpiredattribute 来切换按钮,就像在这个示例中一样:https://github.com/auth0/angular2-jwt#checking-authentication-to-hideshow-elements-and-handle-routing

      它可以让你防止 401...

      【讨论】:

        【解决方案4】:

        嗯,重新加载很简单:(&lt;any&gt;window).location.reload(true);

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

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

        【讨论】:

          【解决方案5】:

          使用浏览器会话:

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

          将数据存储为json字符串,如果请求失败,则重新创建表单数据。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2021-01-10
            • 2015-11-12
            • 1970-01-01
            • 1970-01-01
            • 2018-02-19
            • 1970-01-01
            • 2016-09-27
            相关资源
            最近更新 更多