角度http拦截器重复请求不起作用

时间:2019-12-12 22:06:49

标签: angular typescript rxjs

我正在执行angular 8应用程序,并且尝试实现有角度的http拦截器重复请求。我想拦截对WebAPI的所有UI请求,当令牌过期时,刷新令牌并重新调用所有被拒绝的请求。 我实现了代码:

import { Inject, Injectable, Injector } from '@angular/core';
import {
    HttpInterceptor,
    HttpRequest,
    HttpResponse,
    HttpHandler,
    HttpEvent,
    HttpErrorResponse
} from '@angular/common/http';

import { Observable, throwError, from, Subject } from 'rxjs';
import { tap, map, catchError } from 'rxjs/operators';

import { StorageSvc } from '../factories/storageSvc';
import { SysSettings } from '../constant/sysSettings';
import { SingletonRootClass } from '../../../src/app/shared/singleton-root';
import { AuthService } from './authService';
import { Router } from '@angular/router';

@Injectable()

export class InterceptService implements HttpInterceptor {

    refreshTokenInProgress = false;

    tokenRefreshedSource = new Subject();
    tokenRefreshed$ = this.tokenRefreshedSource.asObservable();


    constructor(@Inject(StorageSvc) public storageSvc: any,
                @Inject(AuthService) public authService: any,
                private router: Router,
                private injector: Injector
    ) { }

    addAuthHeader(request) {
        const authData = this.storageSvc.retrieve('authorizationData');
        const language = authData && authData.profile
            ? { languageCode: authData.profile.laguageCode, regionCode: authData.profile.regionCode }
            : this.getDefaultLanguage();

        const headers = {
            'Accept-Language': this.buildAcceptLanguageHeader(language.languageCode, language.regionCode),
            'NotificationConnectionId': SysSettings.NotifyConnectionId
        };

        if (authData) {
            headers['Authorization'] = 'Bearer ' + authData.token;
            headers['UserName'] = authData.userName;
        }

        request = request.clone({
            setHeaders: headers
        });

        return request;
    }

    // intercept request and add token
    intercept(request: HttpRequest<any>, next: HttpHandler): Observable<any> {
        request = this.addAuthHeader(request);

        // Handle response
        return next.handle(request).pipe(
            map((event: HttpEvent<any>) => {
                if (event instanceof HttpResponse) {
                    console.log('event--->>>', event);
                }
                return event;
            }),
                catchError((error: HttpErrorResponse) => {
                    if (error.status === 401) {
                        return this.refreshToken()
                            .switchMap(() => {
                                request = this.addAuthHeader(request);
                                return next.handle(request);
                            })
                            .catch(() => {
                                this.authService.logOut();
                                this.router.navigate(['/login']);
                            });
                    }

                    return throwError(error);
                })
        );

    }

    refreshToken() {
        if (this.refreshTokenInProgress) {
            return new Observable(observer => {
                this.tokenRefreshed$.subscribe(() => {
                    observer.next();
                    observer.complete();
                });
            });
        } else {
            this.refreshTokenInProgress = true;

            return this.authService.refreshToken()
                .do(() => {
                    this.refreshTokenInProgress = false;
                    this.tokenRefreshedSource.next();
                });
        }
    }

    sleep(ms = 0) {
        return new Promise(r => setTimeout(r, ms));
    }
    getDefaultLanguage() {
        // const savedLanguage = this.storageSvc.retrieve('language');
        const savedLanguage = SingletonRootClass.getInstance().defaultLang;
        // Region code not used for now
        const language = { languageCode: savedLanguage ? savedLanguage : 'en', regionCode: 'CA' };
        return language;
    }

    buildAcceptLanguageHeader(languageCode, regionCode) {
        return languageCode + '-' + regionCode + ',' + languageCode + ';' + 'q=0.8,en;q=0.6';
    }
}

但是我无法自动撤回被拒绝的请求。在我的屏幕截图中:GetNotifications,GetNotificationTypes和GetEventTypes

enter image description here

有人可以建议拦截器使用角度解决方案来调用在刷新令牌的同时被拒绝的所有请求吗? 谢谢

2 个答案:

答案 0 :(得分:0)

尝试这个

  isRefreshingToken: boolean = false;
  tokenSubject: Subject<boolean> = new Subject<boolean>();

  intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    return next.handle(request).pipe(
      catchError((err, source) => {
        if (err instanceof HttpErrorResponse) {
          if (err.status === 401) {
            return this.handle401Error(request, next);
          } 
        }
        return observableThrowError(err.error);
      })
    );
  }

  private handle401Error(request: HttpRequest<any>, next: HttpHandler): Observable<any> {
    this.requestRefreshToken();
    return this.tokenSubject.pipe(
      take(1),
      switchMap(token => {
        return next.handle(request);
      }));

  }

  private requestRefreshToken() {
    if (!this.isRefreshingToken) {
      this.isRefreshingToken = true;
      this.authService.refreshToken()
        .subscribe(user => {
          if (user) {
            this.tokenSubject.next(user);
          }
          this.isRefreshingToken = false;
        }, (err: HttpErrorResponse) => {
          this.authService.logout()
          this.isRefreshingToken = false; 
          return observableThrowError(err);
        }, () => {
        });
    }
  }

答案 1 :(得分:0)

适用于Angular 7的拦截解决方案:

auth-interceptor.ts

import { Inject, Injectable, Injector } from '@angular/core';
import {
    HttpInterceptor,
    HttpRequest,
    HttpResponse,
    HttpHandler,
    HttpEvent,
    HttpErrorResponse
} from '@angular/common/http';

import { Observable, throwError, from, Subject, BehaviorSubject } from 'rxjs';
import { catchError, filter } from 'rxjs/operators';

import { StorageSvc } from '../factories/storageSvc';
import { SysSettings } from '../constant/sysSettings';
import { SingletonRootClass } from '../../../src/app/shared/singleton-root';
import { AuthService } from './authService';
import { Router } from '@angular/router';

import { take, switchMap  } from 'rxjs/operators';

@Injectable()

export class InterceptService implements HttpInterceptor {

    private isRefreshing = false;
    private refreshTokenSubject: BehaviorSubject<any> = new BehaviorSubject<any>(null);

    constructor(@Inject(StorageSvc) public storageSvc: any,
                @Inject(AuthService) public authService: any,
                private router: Router,
                private injector: Injector
    ) { }

    addAuthHeader(request) {
        const authData = this.storageSvc.retrieve('authorizationData');
        const language = authData && authData.profile
            ? { languageCode: authData.profile.laguageCode, regionCode: authData.profile.regionCode }
            : this.getDefaultLanguage();

        const headers = {
            'Accept-Language': this.buildAcceptLanguageHeader(language.languageCode, language.regionCode),
            'NotificationConnectionId': SysSettings.NotifyConnectionId
        };

        if (authData) {
            console.log("token:" + 'Bearer ' + authData.token);
            headers['Authorization'] = 'Bearer ' + authData.token;
            headers['UserName'] = authData.userName;
        }

        request = request.clone({
            setHeaders: headers
        });

        return request;
    }


    intercept(request: HttpRequest<any>, next: HttpHandler): Observable<any> {

        request = this.addAuthHeader(request);

        return next.handle(request).pipe(catchError(error => {
            if (error instanceof HttpErrorResponse && error.status === 401) {
                return this.handle401Error(request, next);
            } else {
                return throwError(error);
            }
        }));
    }

    private handle401Error(request: HttpRequest<any>, next: HttpHandler) {
        if (!this.isRefreshing) {
            this.isRefreshing = true;
            this.refreshTokenSubject.next(null);

            return this.authService.refreshToken().pipe(
                switchMap((token: any) => {
                    this.isRefreshing = false;
                    this.refreshTokenSubject.next(token.access_token);
                    request = this.addAuthHeader(request);
                    return next.handle(request);
                }));

        } else {
            return this.refreshTokenSubject.pipe(
                filter(token => token != null),
                take(1),
                switchMap(jwt => {
                    request = this.addAuthHeader(request);
                    return next.handle(request);
                }));
        }
    }

    sleep(ms = 0) {
        return new Promise(r => setTimeout(r, ms));
    }
    getDefaultLanguage() {
        // const savedLanguage = this.storageSvc.retrieve('language');
        const savedLanguage = SingletonRootClass.getInstance().defaultLang;
        // Region code not used for now
        const language = { languageCode: savedLanguage ? savedLanguage : 'en', regionCode: 'CA' };
        return language;
    }

    buildAcceptLanguageHeader(languageCode, regionCode) {
        return languageCode + '-' + regionCode + ',' + languageCode + ';' + 'q=0.8,en;q=0.6';
    }
}

authService.ts:

import { Inject, Injectable } from '@angular/core';
import { HttpClient, HttpHeaders  } from '@angular/common/http';
import { JwtHelperService } from '@auth0/angular-jwt';

import * as $ from 'jquery';

import { StorageSvc } from '../factories/storageSvc';
import { CrudService } from './crudService';
// import {NotifyHub} from '../../../src/app/angularJS-upgraded-providers';
import { SysSettings } from '../constant/sysSettings';
import { Authentication } from '../model/authentication.model';
import { SingletonRootClass } from '../../../src/app/shared/singleton-root';
import { Router } from '@angular/router';

import { tap } from 'rxjs/operators';

@Injectable()
export class AuthService {
    private serviceBase = SysSettings.WebServiceURL;
    private refreshInProgress = false;
    private refreshPromises = [];
    public authentication = new Authentication();

    constructor(
        private httpClient: HttpClient,
        private storageSvc: StorageSvc,
        private crudSvc: CrudService,
        private router: Router,
        // @Inject(NotifyHub) private notifyHub  //TODO
    ) {

    }


    refreshToken() {
        const authData = this.storageSvc.retrieve('authorizationData');
        const data = 'grant_type=refresh_token&refresh_token=' + authData['refreshToken'] +
            '&client_id=' + SysSettings.clientId;

        return this.httpClient.post(this.serviceBase + 'token', data,
            { headers: { 'Content-Type': 'application/x-www-form-urlencoded' } }).pipe(tap((respData) => {
            const response: any = respData;
            const oldAuthData = this.storageSvc.retrieve('authorizationData');
            const loginTime = oldAuthData['loginTime'] ? new Date(oldAuthData['loginTime']) : new Date();
            const newAuthData = {
                token: response.access_token,
                userId: response.userName,
                userName: response.userName,
                refreshToken: response.refresh_token,
                useRefreshTokens: true,
                loginTime: loginTime
            };
            if (oldAuthData && oldAuthData['profile']) {
                newAuthData['profile'] = oldAuthData['profile'];
            }
            this.storageSvc.store('authorizationData', newAuthData);
        }));
    }

}