Angular 5 HTTPCLIENT Interceptor JWT刷新令牌无法捕获401并重试我的请求



我正在尝试实现401个响应的捕获量,并尝试根据" Angular 4 Interceptor重试"请求获得刷新令牌,以刷新刷新。我试图实施同一件事,但是我从来没有能够重述该请求,我真的不确定这是否是应用刷新令牌策略的最佳方法。这是我的代码:

@Injectable()
export class AuthInterceptorService implements HttpInterceptor {
 public authService;
 refreshTokenInProgress = false;
 tokenRefreshedSource = new Subject();
 tokenRefreshed$ = this.tokenRefreshedSource.asObservable();
 constructor(private router: Router, private injector: Injector) { }
 authenticateRequest(req: HttpRequest<any>) {
 const token = this.authService.getToken();
 if (token != null) {
 return req.clone({
 headers: req.headers.set('Authorization', `Bearer ${token.access_token}`)
 });
 }
 else {
 return null;
 }
 }
 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();
 }).catch(
 (error) => {
 console.log(error);
 }
 );
 }
 }
 intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
 this.authService = this.injector.get(AuthenticationService);
 request = this.authenticateRequest(request);
 return next.handle(request).do((event: HttpEvent<any>) => {
 if (event instanceof HttpResponse) {
 // do stuff with response if you want
 }
 }, (err: any) => {
 if (err instanceof HttpErrorResponse) {
 if (err.status === 401) {
 return this.refreshToken()
 .switchMap(() => {
 request = this.authenticateRequest(request);
 console.log('*Repeating httpRequest*', request);
 return next.handle(request);
 })
 .catch(() => {
 return Observable.empty();
 });
 }
 }
 });
 }
}

问题是从未达到SwitchMap ...

if (err.status === 401) {
 return this.refreshToken()
 .switchMap(() => {

和DO操作员...

return this.authService.refreshToken()
 .do(() => {

这样,这将我带到了我的authservice刷新方法...

refreshToken() {
 let refreshToken = this.getToken();
 refreshToken.grant_type = 'refresh_token';
 refreshToken.clientId = environment.appSettings.clientId;
 return this.apiHelper.httpPost(url, refreshToken, null)
 .map
 (
 response => {
 this.setToken(response.data, refreshToken.email);
 return this.getToken();
 }
 ).catch(error => {
 return Observable.throw('Please insert credentials');
 });
 }
 }

它返回可观察的映射,我知道如果我替换了...

return this.authService.refreshToken()
 .do(() => {

用订阅,我猜我会打破可观察到的链。我迷路了,我已经没有解决方案了很长时间。:D

我很高兴您喜欢我的解决方案。我将在此处仅放置最终解决方案,但是如果有人想知道我下载的过程:刷新oauth oauth authentication angular 4

好吧,首先,我创建了一个服务来保存刷新令牌请求的状态,并可以观察到何时完成请求。

这是我的服务:

@Injectable()
export class RefreshTokenService {
  public processing: boolean = false;
  public storage: Subject<any> = new Subject<any>();
  public publish(value: any) {
    this.storage.next(value);
  }
}

我注意到,如果我有两个拦截器一个可以刷新令牌并处理该授权标题,那就更好了。

这是刷新令牌的拦截器:

@Injectable()
  export class RefreshTokenInterceptor implements HttpInterceptor {
    constructor(private injector: Injector, private tokenService: RefreshTokenService) {
    }
    intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
      const auth = this.injector.get(OAuthService);
      if (!auth.hasAuthorization() && auth.hasAuthorizationRefresh() && !this.tokenService.processing && request.url !== AUTHORIZE_URL) {
        this.tokenService.processing = true;
        return auth.refreshToken().flatMap(
          (res: any) => {
            auth.saveTokens(res);
            this.tokenService.publish(res);
            this.tokenService.processing = false;
            return next.handle(request);
          }
        ).catch(() => {
          this.tokenService.publish({});
          this.tokenService.processing = false;
          return next.handle(request);
        });
      } else if (request.url === AUTHORIZE_URL) {
        return next.handle(request);
      }
      if (this.tokenService.processing) {
        return this.tokenService.storage.flatMap(
          () => {
            return next.handle(request);
          }
        );
      } else {
        return next.handle(request);
      }
    }
  }

所以我在这里等待刷新令牌可用或失败,然后发布需要授权标题的请求。

这是放置授权标题的拦截器:

@Injectable()
  export class TokenInterceptor implements HttpInterceptor {
    constructor(private injector: Injector) {}
    intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
      const auth = this.injector.get(OAuthService);
      let req = request;
      if (auth.hasAuthorization()) {
        req = request.clone({
          headers: request.headers.set('Authorization', auth.getHeaderAuthorization())
        });
      }
      return next.handle(req).do(
        () => {},
        (error: any) => {
          if (error instanceof HttpErrorResponse) {
            if (error.status === 401) {
              auth.logOut();
            }
          }
        });
    }
  }

和我的主模块是这样的:

@NgModule({
  imports: [
    ...,
    HttpClientModule
  ],
  declarations: [
    ...
  ],
  providers: [
    ...
    OAuthService,
    AuthService,
    RefreshTokenService,
    {
      provide: HTTP_INTERCEPTORS,
      useClass: RefreshTokenInterceptor,
      multi: true
    },
    {
      provide: HTTP_INTERCEPTORS,
      useClass: TokenInterceptor,
      multi: true
    }
  ],
  bootstrap: [AppComponent]
})
export class AppModule {
}

请欢迎任何反馈,如果我放错了,请告诉我。我正在使用 Angular 4.4.6 进行测试,但我不知道它在Angular 5上是否有效,我认为应该有效。

下面的拦截器为您执行此任务

import {
  throwError as observableThrowError,
  Observable,
  Subject,
  EMPTY,
} from 'rxjs';
import { catchError, switchMap, tap, finalize } from 'rxjs/operators';
import { Injectable } from '@angular/core';
import {
  HttpInterceptor,
  HttpRequest,
  HttpHandler,
  HttpSentEvent,
  HttpHeaderResponse,
  HttpProgressEvent,
  HttpResponse,
  HttpUserEvent,
  HttpErrorResponse,
} from '@angular/common/http';
import { StoreService } from './store.service';
import { ApiService } from './api.service';
export const tokenURL = '/315cfb2a-3fdf-48c3-921f-1d5209cb7861'; //copied from api service
@Injectable()
export class SessionInterceptorService implements HttpInterceptor {
  isRefreshingToken: boolean = false;
  cachedRequests = [];
  tokenSubject: Subject<string> = new Subject<string>();
  constructor(
    private readonly store: StoreService,
    private readonly ApiService: ApiService
  ) {}
  intercept(
    req: HttpRequest<any>,
    next: HttpHandler
  ): Observable<
    | HttpSentEvent
    | HttpHeaderResponse
    | HttpProgressEvent
    | HttpResponse<any>
    | HttpUserEvent<any>
  > {
    let urlPresentIndex = this.cachedRequests.findIndex(
      (httpRequest) => httpRequest.url == req.url
    );
    if (this.isRefreshingToken && !req.url.endsWith(tokenURL)) {
      // check if unique url to be added in cachedRequest
      if (urlPresentIndex == -1) {
        this.cachedRequests.push(req);
        return this.tokenSubject.pipe(
          switchMap(() => next.handle(req)),
          tap((v) => {
            // delete request from catchedRequest if api gets called
            this.cachedRequests.splice(
              this.cachedRequests.findIndex(
                (httpRequest) => httpRequest.url == req.url
              ),
              1
            );
            return EMPTY;
          })
        );
      } else {
        //already in cached request array
        return EMPTY;
      }
    }
    return next.handle(this.updateHeader(req)).pipe(
      catchError((error) => {
        console.log(error);
        if (error instanceof HttpErrorResponse) {
          switch ((<HttpErrorResponse>error).status) {
            case 400:
              return this.handle400Error(error);
            case 403 || 401:
              if (req.url.endsWith(tokenURL)) {
                return observableThrowError(error);
              } else {
                this.cachedRequests.push(req);
                return this.handle401Error(req, next);
              }
            default:
              return observableThrowError(error);
          }
        } else {
          return observableThrowError(error);
        }
      })
    );
  }
  handle400Error(error) {
    if (
      error &&
      error.status === 400 &&
      error.error &&
      error.error.error === 'invalid_grant'
    ) {
      // If we get a 400 and the error message is 'invalid_grant', the token is no longer valid so logout.
      return this.logout();
    }
    return observableThrowError(error);
  }
  handle401Error(req: HttpRequest<any>, next: HttpHandler) {
    if (!this.isRefreshingToken) {
      this.isRefreshingToken = true;
      return this.ApiService.refreshToken().pipe(
        switchMap((newToken: string) => {
          if (newToken) {
            this.store.updateAccessToken(newToken);
            this.tokenSubject.next(newToken);
            return next.handle(this.updateHeader(this.cachedRequests[0]));
          }
          // If we don't get a new token, we are in trouble so logout.
          return this.logout();
        }),
        catchError((error) => {
          // If there is an exception calling 'refreshToken', bad news so logout.
          return this.logout();
        }),
        finalize(() => {
          this.isRefreshingToken = false;
        })
      );
    }
  }
  logout() {
    console.log('logging it out');
    // Route to the login page (implementation up to you)
    return observableThrowError('');
  }
  /*
        This method is append token in HTTP request'.
    */
  updateHeader(req) {
    const authToken = this.store.getAccessToken();
    console.log(authToken);
    req = req.clone({
      headers: req.headers.set('X-RapidAPI-Key', `${authToken}`),
    });
    return req;
  }
}

有关更多详细信息,您可以阅读我的媒介文章token-refresh-Interceptor-retry-failed-requeSts

检查一下,它如何工作stackblitz

相关内容

  • 没有找到相关文章

最新更新