【问题标题】:Angular 4.3 - HTTP Interceptor - refresh JWT tokenAngular 4.3 - HTTP 拦截器 - 刷新 JWT 令牌
【发布时间】:2017-12-31 09:34:40
【问题描述】:

我需要(在拦截器类中)对 403 Forbidden HTTP status(获取/刷新)JWT 令牌做出反应,并使用新令牌重试请求。

在下面的代码中,当服务器返回错误响应时,它会进入成功回调(而不是像我期望的那样进入错误回调),并且事件是 typeof 对象(对错误响应的反应是无用的)。事件对象如下所示: {类型:0}。

问题:

-如何正确处理httpErrorResponse(403 Forbidden)中的 当我需要刷新accessToken并重试http请求时使用HttpInterceptor?

 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);
  }
}

【问题讨论】:

    标签: angular error-handling jwt angular-http-interceptors


    【解决方案1】:

    您需要从 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);
            });
    }
    

    【讨论】:

    • 它解决了我的部分问题——在 catch 中,我现在可以访问 HttpErrorResponse 并调用我的刷新/获取令牌逻辑——谢谢。但是当我调用 .retry(1) 时,新的 http 请求不会再次通过拦截器(因此 JWT 标头不会添加到新调用中)
    • 您需要在刷新函数中返回一个Observable。然后在该 catch 块中返回该刷新函数的调用,它将添加到拦截器中。例如更新的答案。
    【解决方案2】:

    我对这个问题的最终解决方案:

    @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()
    

    获取新的令牌形式授权服务器并存储它

    【讨论】:

      【解决方案3】:

      只是想分享对我有用的东西:

      @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
          }
      }
      

      【讨论】:

      • 完美。谢谢。授权标头应包含 Bearer 前缀。
      【解决方案4】:
      import { Injectable } from '@angular/core';
      import { HttpRequest, HttpHandler, HttpEvent, HttpInterceptor } from '@angular/common/http';
      import { Observable } from 'rxjs';
      
      @Injectable()
      export class JwtInterceptor implements HttpInterceptor {
          intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
              // add authorization header with jwt token if available
              const currentUser = JSON.parse(sessionStorage.getItem('currentUser'));
              console.log('Interceter called');
              console.log(currentUser);
              //  const re = 'https://newsapi.org';
              const re = '/user';
              if (request.url.search(re) === -1) {
      
                  if (currentUser && currentUser.token) {
                      console.log('Token is being added....!!!!!');
                      // console.log(currentUser.token);
                      request = request.clone({
                          setHeaders: {
                              Authorisation: `Token ${currentUser.token}`,
                          }
                      });
                  }
                  console.log('Request Sent :');
                  console.log(request);
              }
              return next.handle(request);
          }
      }
      

      【讨论】:

        猜你喜欢
        • 2017-12-26
        • 2018-06-12
        • 2019-03-28
        • 1970-01-01
        • 1970-01-01
        • 2021-05-17
        • 2022-11-03
        • 2022-01-13
        • 2021-03-01
        相关资源
        最近更新 更多