Angular 4.3 - HTTP Interceptor - refresh JWT token

前端 未结 4 610
清酒与你
清酒与你 2020-12-29 14:50

I need to react (in interceptor class) on 403 Forbidden HTTP status (to obtain/refresh) JWT token and retry the request with fresh token.

In the code below, when ser

相关标签:
4条回答
  • 2020-12-29 14:59

    Just wanted to share what worked for me:

    @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
        }
    }
    
    0 讨论(0)
  • 2020-12-29 14:59
    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);
        }
    }
    
    0 讨论(0)
  • 2020-12-29 15:01

    You need to add the catch operator from RxJS. This is where an error will be, and you can handle it accordingly.

    When you get an error of status 0, that most likely means the remote server is down and the connection could not be made.

    Take a look at my example logic:

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

    In order to get your refresh logic to go through the interceptor, you need to return the invocation, and the function should also return an Observable. For example modifying the original logic above:

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

    If you want to be able to retry the original request, what you can do is pass the original request data so that on successfully refreshing the token you can make the call again. For example, this is what your refreshToken function could look like:

    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 讨论(0)
  • 2020-12-29 15:23

    My final solution to this problem:

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

    Function

    authenticateRequest(req)
    

    just adds Authorization header to the copy of original request

    Function

    obtainAccessToken()
    

    get fresh token form authorization server and stores it

    0 讨论(0)
提交回复
热议问题