2017-07-25 94 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 
    } 
}