首页 文章

Angular 5 HttpClient拦截器JWT刷新令牌无法捕获401并重试我的请求

提问于
浏览
5

我正在尝试实现401响应的捕获并尝试获取基于Angular 4 Interceptor retry requests after token refresh的刷新令牌 . 我试图实现同样的事情,但我从来没有能够重试该请求,我真的不确定这是否是应用刷新令牌策略的最佳方法 . 这是我的代码:

@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方法......

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

它返回一个映射的observable,我知道如果我替换了do in,它需要订阅...

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

With subscribe I'll break the observable chain I guess. I am lost and I've playing with this for a long time without a solution. :D

1 回答

  • 3

    我想在这里提出最终的解决方案,但是如果有人想知道我休会的过程请到这里:Refresh Token OAuth Authentication Angular 4+

    好的,首先我创建了一个服务来保存 refresh token request 和Observable的状态,以便知道请求何时完成 .

    这是我的服务:

    @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 进行测试,但我不知道它是否适用于角度5,我认为应该可行 .

相关问题