angular 自定义 http 请求前 2 个刷新令牌

angular 2 refresh token before custom http request

如果令牌已过期,我想在自定义 http 请求之前刷新令牌。当我确定令牌已过期但它给出以下控制台结果时,我尝试了我的代码:

Token refresh is required  app.js:1:92855
updateToken() method inside  app.js:1:93301
tokenNotExpired?: false  app.js:1:92674
Token refresh is required  app.js:1:92855
updateToken() method inside  app.js:1:93301
tokenNotExpired?: false  app.js:1:92674
Token refresh is required  app.js:1:92855
updateToken() method inside  app.js:1:93301
tokenNotExpired?: false  app.js:1:92674
Token refresh is required  app.js:1:92855
updateToken() method inside  app.js:1:93301
tokenNotExpired?: false  app.js:1:92674
............ lots of the same sentences and finally exception:

EXCEPTION: Uncaught (in promise): Error: Error in :0:0 caused by: too much recursion
k@http://localhost/xxx/node_modules/zone.js/dist/zone.min.js:1:11750
............

据我所知,在刷新令牌期间它会进入无限循环。我已经在其他地方用一个按钮测试了 updateToken() 方法,它工作正常。

我做错了什么?

自定义 http 服务

import { Injectable } from '@angular/core';
import { Http, XHRBackend, RequestOptions, Request, RequestOptionsArgs, Response, Headers } from '@angular/http';
import { tokenNotExpired } from "angular2-jwt";
import { Observable } from "rxjs/Observable";

@Injectable()
export class HttpService extends Http {

    constructor (backend: XHRBackend, options: RequestOptions) {
        let token = localStorage.getItem('access_token'); // your custom token getter function here
        options.headers.set('Authorization', `Bearer ${token}`);
        super(backend, options);
    }

    request(url: string|Request, options?: RequestOptionsArgs): Observable<Response> {
        let token = localStorage.getItem('access_token');

        if (typeof url === 'string') { // meaning we have to add the token to the options, not in url
            if (!options) {
                // let's make option object
                options = {headers: new Headers()};
            }
            options.headers.set('Authorization', `Bearer ${token}`);
        } else { // we have to add the token to the url object
            url.headers.set('Authorization', `Bearer ${token}`);
        }

        console.log("tokenNotExpired?: " + tokenNotExpired('access_token'));

        if(tokenNotExpired('access_token')){ // if token is NOT expired

            return super.request(url, options).catch(this.catchAuthError(this));

        }else{  // if token is expired
            console.log("Token refresh is required");
            return this.updateToken()
                .flatMap((result: boolean) => {
                    console.log("updateToken result");
                    console.log(result);
                    if (result) {
                        return super.request(url, options).catch(this.catchAuthError(this));
                    } else {
                        return Observable.throw(new Error('Can\'t refresh the token'));
                    }

                });
        }
    }

    updateToken(): Observable<any> {

        console.log("updateToken() method inside");

        let body: string = 'refresh_token=' + localStorage.getItem('refresh_token') + '&client_id=' + localStorage.getItem('resource')  + '&grant_type=refresh_token';

        return super.post(
            localStorage.getItem("authUrl"),
            body,
            new RequestOptions({headers: new Headers({ 'Content-Type': 'application/x-www-form-urlencoded' })})
        )
            .map((response: Response) => {
                let returnedBody: any = response.json();
                console.log("post returnedBody");
                console.log(returnedBody);
                if (typeof returnedBody.access_token !== 'undefined'){
                    localStorage.setItem('access_token', returnedBody.access_token);
                    localStorage.setItem('refresh_token', returnedBody.refresh_token);

                    console.log("Token Refreshed: refreshed access_token");
                    console.log(localStorage.getItem('access_token'));

                    return true;
                }
                else {
                    return false;
                }
            });
    }

    private catchAuthError (self: HttpService) {
        return (res: Response) => {
            console.log(res);
            return Observable.throw(res);
        };
    }
}

应用模块

@NgModule({
  imports: [ .......... ],
  declarations: [ ....... ],
  providers: [
    {
      provide: HttpService,
      useFactory: (backend: XHRBackend, options: RequestOptions) => {
        return new HttpService(backend, options);
      },
      deps: [XHRBackend, RequestOptions]
    }

  ],
  bootstrap: [ Application ]
})

在您的 updateToken 方法中,您正在调用 super.post,这相当于 Http.prototype.post.apply(this...),并且 super.post 将在内部调用 this.request().

this 上下文是您的自定义 HttpService,它以 HttpService request 方法的递归调用结束。您应该改为调用 super.request :

return super.request(
    new Request({
      method: RequestMethod.Post,
      url: localStorage.getItem("authUrl"),
      body,
      headers: new Headers({
        'Content-Type': 'application/x-www-form-urlencoded'
      })
    })

  )
  .map((response: Response) => {
    let returnedBody: any = response.json();
    console.log("post returnedBody");
    console.log(returnedBody);
    if (typeof returnedBody.access_token !== 'undefined') {
      localStorage.setItem('access_token', returnedBody.access_token);
      localStorage.setItem('refresh_token', returnedBody.refresh_token);

      console.log("Token Refreshed: refreshed access_token");
      console.log(localStorage.getItem('access_token'));

      return true;
    } else {
      return false;
    }
  });

另请注意,创建自定义 Http 服务可能不是最佳主意。

但也许您可以创建一个可以注入 http 的服务,因为您可能不需要通过身份验证就可以从 ajax 调用中获取一些简单的静态数据。

这也可以避免您遇到的超出递归调用堆栈的问题。

@Injectable()
export class MyAuhtHttpService{

  constructor(private http:Http){}

}