繁体   English   中英

Angular 4.3 - HTTP 拦截器 - 刷新 JWT 令牌

[英]Angular 4.3 - HTTP Interceptor - refresh JWT token

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

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

问题:

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

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

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

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

只需将 Authorization 标头添加到原始请求的副本中

功能

obtainAccessToken()

获取新的令牌表单授权服务器并存储它

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

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM