2017-10-17 10 views
0

グローバルトークンを使用してアクセストークンの有効期限を処理し、リフレッシュトークンを使用してリフレッシュする必要があります。次のようにほとんど働くようで私のソリューションは、次のとおりです。角2:グローバルベースでhttp oauth authtoken 401エラーを処理する方法

ROMコンポーネント、次のように観察を購読する:

this.myHttpService.getSomeData().subscribe(response => { 
       console.log('user logged in', response); 
     }, 
     error => { 
     // optionally do something custom here. 
     console.log(error); 
     throw error; 
     // we could handle the error here, getting the new auth token and calling this again. 
    } 

我々はグローバルにエラーを処理することができるので、上記のエラーハンドラは、必要ありませんエラーハンドラですが、ここで何か他のものを処理したいかもしれません。 1.キャッチエラー 2がいることを認識:

getSomeData(): Observable<any> { 
    return this.http.get(this.API_URL_BASE + '/api/activities/getsomeData, this.httpHelperMethodsService.defaultOptions).map((response: Response) => {  
     return response.json(); 
    }).catch(this.handleError); 
    } 

トークンは次のようになります期限切れ認証を処理するためのプロセス:私のHTTPサービスで

、私はこのように観察を返す機能を持っています

:認証トークンは 3.呼び出し、このようになりますこれは、ハンドルの誤差関数を実装し、サービスで再実行してソースに観察

4. リフレッシュトークンを使用して、新しい認証トークンを取得するには、期限が切れています

private handleError(error: any, originalObservable: any) { 
    let errorHelper = { 'error': error, 'observable': originalObservable}; 
    return Observable.throw(errorHelper); 
} 

次のようにして、その後、グローバルエラーハンドラを作成することができます

@Injectable() 
export class ApplicationErrorHandler extends ErrorHandler { 

constructor(private injector: Injector) { 
    super(false); 
    } 

    handleError(error: any): void { 
    const refreshTokenService = this.injector.get(RefreshTokenService); 
     ... 
     else if(error.statusText == "Unauthorized"){ 
      // check if token is expired. This will involve looking in local storage and comparing expiry time to current time. 
      if(isTokenExpired){ 
       refreshTokenService.refreshToken(refreshToken).subscribe(response => { 
        // set refresh token 
         error.observable().subscribe(response => { 
         // return response. 
         }, error => { 
         // return error. 
         }) 
       } 
      } 
    } 
    } 

}

次のように構成されたHTTP-インターセプタを使用します。

import {Injectable} from "@angular/core"; 
import { ConnectionBackend, RequestOptions, Request, RequestOptionsArgs, Response, Http, Headers} from "@angular/http"; 
import {Observable} from "rxjs/Rx"; 


@Injectable() 
export class InterceptedHttp extends Http { 

    constructor(backend: ConnectionBackend, defaultOptions: RequestOptions) { 
     super(backend, defaultOptions); 
    } 

    request(url: string | Request, options?: RequestOptionsArgs): Observable<Response> { 
     return super.request(url, options); 
    } 

    get(url: string, options?: RequestOptionsArgs): Observable<Response> { 
     return super.get(url, this.getRequestOptionArgs(options)); 
    } 

    post(url: string, body: string, options?: RequestOptionsArgs): Observable<Response> { 
     return super.post(url, body, this.getRequestOptionArgs(options)); 
    } 

    put(url: string, body: string, options?: RequestOptionsArgs): Observable<Response> { 
     return super.put(url, body, this.getRequestOptionArgs(options)); 
    } 

    delete(url: string, options?: RequestOptionsArgs): Observable<Response> { 
     return super.delete(url, this.getRequestOptionArgs(options)); 
    } 

    private getRequestOptionArgs(options?: RequestOptionsArgs) : RequestOptionsArgs { 
     return this.getOptionsWithAccessToken(); 
    } 

    getOptionsWithAccessToken() { 
     let accessToken = localStorage.getItem('access_token'); 
     let accessTokenJson = <any>JSON.parse(accessToken); 
     let headers = new Headers(); 
     headers.append('Content-Type', 'application/json'); 
     headers.append('Accept', 'application/json'); 
     headers.append('Authorization', accessTokenJson); 
     let options = new RequestOptions({ headers: headers }); 
     console.log('options updated', options); 
     return options; 
     } 
} 

にhttpしかし、インターセプタは、元のobservableに再度サブスクライブするときに呼び出されるのではなく、別の401が取得されます。ログ文console.log('options updated', options);このインターセプタは他のすべてのhttpコールの前に呼び出されています。元のobservableを購読すると、httpインターセプタは実行されないようですが、これは本当ですか?

新しい監視トークンを使用するために、この監視対象に関するオプションを更新する方法はありますか?おそらく、私は、オブザーバブルではなく、オブザーバブル上の他のメソッドの1つを呼び出すことができますか?またはこれは不可能ですか?私が考えることのできる唯一の選択肢は、httpコールごとにエラーをキャッチすることですが、私は約100個のコールを持っています。

+2

https://angular.io/guide/http#intercepting-all-requests-or-responses –

+0

ありがとうございました。はい、解決策のように聞こえます。インターセプタに認証トークンが設定されている場合は、期待どおりに設定されます。 私は今家に向かいますので、明日これを試してみましょう。 – Sam

+0

@JBNizet httpインターセプタを使用しようとする試みを含めるように質問を更新しましたが、残念ながらこれは機能しませんでした。 – Sam

答えて

0

HttpからHttpClientにアップグレードしました。次に、新しいhttpInterceptorを実装してInjectorを渡して他のサービスにアクセスし、各リクエストでトークンがすぐに期限切れになっているかどうかを確認し、そうであればそれをリフレッシュし、そうでなければエラーをキャッチします。

@Injectable() 
export class MyInterceptor implements HttpInterceptor { 
    API_URL_BASE; 
    isCurrentlyRefreshing: boolean = false; 

    constructor(private injector: Injector) { 
     this.API_URL_BASE = myGlobals.API_GLOBAL_VAR; 
    } 

    intercept(
     req: HttpRequest<any>, 
     next: HttpHandler 
    ): Observable<HttpEvent<any>> { 

     if (!this.isCurrentlyRefreshing) { 
      var isTokenRefreshRequired = this.checkIfRefreshRequired(); 
      if (isTokenRefreshRequired) { 
       this.refreshToken().subscribe(response => { 
        this.updateDetailsAfterLogin(response); 

        const httpService = this.injector.get(HttpHelperMethodsService); 
        httpService.loginFromRefreshToken(); 
        this.isCurrentlyRefreshing = false; 
        console.log('response', response); 
       }, error => { 
        this.isCurrentlyRefreshing = false; 
       }); 
      } 
     } 


     return next.handle(req).do(evt => { 

     }, err => { 
      if (err instanceof HttpErrorResponse && err.status == 401) { 
       let toastr = this.injector.get(TOASTR_TOKEN); 

       this.refreshToken().subscribe(response => { 
       this.updateDetailsAfterLogin(response); 
       const httpService = this.injector.get(HttpHelperMethodsService); 
       httpService.loginFromRefreshToken(); 
       toastr.info('User session refreshed following timeout. Please retry.'); 
      }, error => { 
       if(error.error){ 
        if(error.error.error == "invalid_grant"){ 
         toastr.error("session timed out"); 
         var userService = this.injector.get(UserService); 
         var router = this.injector.get(Router); 
         userService.logout(); 
         router.navigate(['/login']); 
        } 
       } 
      }) 
     } 
    }); 
} 

私はそれが可能なトークンをリフレッシュした後reqを再送信することで、ここからプットやポスト要求をrefireが判明一方で、応答は、初期の観察者にリンクしませんでした。私は、これらのまれなケースでトーストを表示するのは大丈夫だろうと決めました。

関連する問題