2017-07-25 91 views
2

我需要對403 Forbidden HTTP狀態(獲取/刷新)JWT令牌作出反應(在攔截器類中),然後使用新令牌重試該請求。Angular 4.3 - HTTP攔截器 - 刷新JWT令牌

在下面的代碼中,當服務器返回錯誤響應時,它返回到成功回調(而不是像我預期的那樣進入錯誤回調),事件是typeof對象(對錯誤響應的反應無用)。事件對象如下所示: {type:0}。

問:

- 如何正確處理httpErrorResponse(403禁止)在 HttpInterceptor當我需要刷新的accessToken並重試http請求?

import { 
    HttpInterceptor, 
    HttpRequest, 
    HttpResponse, 
    HttpHandler, 
    HttpEvent 
} from '@angular/common/http'; 
import 'rxjs/add/operator/map'; 

@Injectable() 
class JWTInterceptor implements HttpInterceptor { 

    constructor(private tokenService: TokenService) {} 
    intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> { 
    let myHeaders = req.headers; 
    if (this.tokenService.accessToken) { 
     myHeaders = myHeaders.append('Authorization',`${this.tokenService.accessToken.token_type} ${this.tokenService.accessToken.access_token}`) 
    } 

    const authReq = req.clone({headers: myHeaders}); 

    return next.handle(authReq).map((event: HttpEvent<any>) => { 
     if (event instanceof HttpResponse) { 
     // success callback 
     } 
    }, (err: any) => { 
     if (err instanceof HttpErrorResponse { 
     if (err.status === 403) { 
      // error callback 
      this.tokenService.obtainAccessToken() 
     } 
     } 
    }) 
     .retry(1); 
    } 
} 

回答

6

我對這個問題的最終解決方案:

@Injectable() 
export class WebApiInterceptor implements HttpInterceptor { 
    constructor(private tokenService: TokenService) { 
    } 

    intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> { 
    console.log('*An intercepted httpRequest*', req, this.tokenService.accessToken); 
    const authReq = this.authenticateRequest(req); 
    console.log('*Updated httpRequest*', authReq); 
    return next.handle(authReq) 
     .map((event: HttpEvent<any>) => { 
     if (event instanceof HttpResponse) { 
      console.log('*An intercepted httpResponse*', event); 
      return event; 
     } 
     }) 
     .catch((error: any) => { 
     if (error instanceof HttpErrorResponse) { 
      if (error.status === 403 && error.url !== environment.authEndpoint) { 
      return this.tokenService 
       .obtainAccessToken() 
       .flatMap((token) => { 
       const authReqRepeat = this.authenticateRequest(req); 
       console.log('*Repeating httpRequest*', authReqRepeat); 
       return next.handle(authReqRepeat); 
       }); 
      } 
     } else { 
      return Observable.throw(error); 
     } 
     }) 
    } 
} 

功能

authenticateRequest(req) 

只是增加了授權頭的副本原始要求

功能

obtainAccessToken() 

得到新鮮的令牌形式的授權服務器,並將其存儲

4

您需要從RxJS中添加catch運算符。這是錯誤發生的地方,你可以相應地處理它。

當您收到狀態爲0的錯誤時,最有可能意味着遠程服務器關閉,無法建立連接。

看看我的例子邏輯:

this.http.request(url, options) 
     .map((res: Response) => res.json()) 
     .catch((error: any) => { 
      const err = error.json(); 

      // Refresh JWT 
      if (err.status === 403) { 
       // Add your token refresh logic here. 
      } 

      return Observable.throw(err); 
     }); 

爲了讓您的刷新邏輯要經過攔截器,你需要返回調用,並且功能上也應返回一個Observable。例如修改上面的原始邏輯:

this.http.request(url, options) 
     .map((res: Response) => res.json()) 
     .catch((error: any) => { 
      const err = error.json(); 

      // Refresh JWT 
      if (err.status === 403) { 
       // refreshToken makes another HTTP call and returns an Observable. 
       return this.refreshToken(...); 
      } 

      return Observable.throw(err); 
     }); 

如果你希望能夠重試原來的請求,你可以做的是通過原來的請求數據,這樣就成功刷新令牌可以再次撥打電話。例如,這是你refreshToken功能可能是什麼樣子:

refreshToken(url: stirng, options: RequestOptionsArgs, body: any, tokenData: any): Observable<any> 
    return this.post(`${this.url}/token/refresh`, tokenData) 
     .flatMap((res: any) => { 
      // This is where I retry the original request 
      return this.request(url, options, body); 
     }); 
} 
+0

它解決我的問題的一部分 - 在抓我現在可以訪問HttpErrorResponse和調用我的刷新/獲取令牌邏輯 - 謝謝你。但是當我調用**。retry(1)**時,新的http請求不會再通過攔截器(因此JWT頭文件不會添加到新的調用中) – kuceraf

+0

您需要在刷新函數中執行的操作是返回'Observable '。然後在該catch塊中返回該刷新函數的調用,並將其添加到攔截器。例如更新答案。 – Lansana

0

只是想分享什麼爲我工作:

@Injectable() 
export class AutoReLoginInterceptor implements HttpInterceptor { 

    constructor() { 
    } 

    intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> { 
     // as we want to intercept the possible errors, instead of directly returning the request execution, we return an Observable to control EVERYTHING 
     return new Observable<HttpEvent<any>>(subscriber => { 

      // first try for the request 
      next.handle(req) 
       .subscribe((event: HttpEvent<any>) => { 
         if (event instanceof HttpResponse) { 
          // the request went well and we have valid response 
          // give response to user and complete the subscription 
          subscriber.next(event); 
          subscriber.complete(); 
         } 
        }, 
        error => { 
         if (error instanceof HttpErrorResponse && error.status === 401) { 
          console.log('401 error, trying to re-login'); 

          // try to re-log the user 
          this.reLogin().subscribe(authToken => { 
           // re-login successful -> create new headers with the new auth token 
           let newRequest = req.clone({ 
            headers: req.headers.set('Authorization', authToken) 
           }); 

           // retry the request with the new token 
           next.handle(newRequest) 
            .subscribe(newEvent => { 
             if (newEvent instanceof HttpResponse) { 
              // the second try went well and we have valid response 
              // give response to user and complete the subscription 
              subscriber.next(newEvent); 
              subscriber.complete(); 
             } 
            }, error => { 
             // second try went wrong -> throw error to subscriber 
             subscriber.error(error); 
            }); 
          }); 
         } else { 
          // the error was not related to auth token -> throw error to subscriber 
          subscriber.error(error); 
         } 
        }); 
     }); 

    } 

    /** 
    * Try to re-login the user. 
    */ 
    private reLogin(): Observable<string> { 
     // obtain new authorization token and return it 
    } 
}