Angular 4和OAuth-截获401响应,刷新访问令牌并重试请求

Angular 4和OAuth-截获401响应,刷新访问令牌并重试请求,angular,typescript,angular-http-interceptors,Angular,Typescript,Angular Http Interceptors,正如标题所说,我正在使用OAuth身份验证进行Angular 4项目 每当http请求以状态代码401响应时,我将拦截该请求,续订访问令牌并重试失败的请求 当我收到401时,请求被正确截获,访问令牌被刷新。失败的请求也会再次执行,但不再将其响应传递给组件 因此,问题在于,我的组件(应该在请求响应上进行观察)在刷新令牌和重试请求之前,会抱怨视图的日志“接收属性时出错” 我的拦截器: import { Injectable, Inject, Injector } from '@angular/cor

正如标题所说,我正在使用OAuth身份验证进行Angular 4项目

每当http请求以状态代码401响应时,我将拦截该请求,续订访问令牌并重试失败的请求

当我收到401时,请求被正确截获,访问令牌被刷新。失败的请求也会再次执行,但不再将其响应传递给组件

因此,问题在于,我的组件(应该在请求响应上进行观察)在刷新令牌和重试请求之前,会抱怨视图的日志“接收属性时出错”

我的拦截器:

import { Injectable, Inject, Injector } from '@angular/core';
import {
  HttpRequest,
  HttpHandler,
  HttpResponse,
  HttpErrorResponse,
  HttpEvent,
  HttpInterceptor,
  HttpSentEvent,
  HttpHeaderResponse,
  HttpProgressEvent,
  HttpUserEvent
} from '@angular/common/http';
import { Observable } from 'rxjs/Observable';
import { AuthService } from './auth.service';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
import { TokenManager } from '../../../util/TokenManager';
import { AuthUserResponse } from '../../../models/authUserResponse';
import 'rxjs/add/operator/switchMap';

@Injectable()
export class AuthTokenExpiredInterceptor implements HttpInterceptor {

  isRefreshingToken: boolean = false;
  tokenSubject: BehaviorSubject<string> = new BehaviorSubject<string>(null);

  constructor( private injector: Injector, private tokenManager: TokenManager ) {}


  intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpSentEvent | HttpHeaderResponse | HttpProgressEvent | HttpResponse<any> | HttpUserEvent<any>> {
    return next.handle(this.addNewAccessTokenToHeaders(request, this.tokenManager.retrieveAccessToken()))
    .do((event: HttpEvent<any>) => {
      if (event instanceof HttpResponse) {
          console.log('processing response', event);
      }
      return event;
    },(err) => {
      if (err instanceof HttpErrorResponse) {
        if (err.status === 401) {
          console.log('Access_token possibly expired, trying to retrieve a new one!')

          return this.handle401Error(request, next);
        } else if (err.status === 400) {
          console.log('Refresh_token possibly expired, redirecting to login');

          return this.handle400Error(err);
        }
      } else {
        return Observable.throw(err);
      }
    });
  }

  handle401Error(request: HttpRequest<any>, next: HttpHandler) {
    if (!this.isRefreshingToken) {
      console.log('in if');
      this.isRefreshingToken = true;

      // Reset here so that the following requests wait until the token comes back from the refreshToken call.
      this.tokenSubject.next(null);

      console.log('About to call renewAccessToken');
      return this.injector.get(AuthService).renewAccessToken().subscribe((response) => {
        let newToken = response.access_token;

        if (newToken) {
          console.log('Got the new access_token!');
          this.tokenSubject.next(newToken);
          let requestToRetry = this.addNewAccessTokenToHeaders(request, newToken);
          console.log('The retried request header: ' + requestToRetry.headers.get("Authorization"));
          return next.handle(requestToRetry);
        } else {  // No token in response
          this.injector.get(AuthService).logout();
        }
      },
      (err) => {
        this.injector.get(AuthService).logout();
        return Observable.throw(err)
      },
      () => {
        console.log('handle401Error done');
        this.isRefreshingToken = false;
      })        
    } else {
      console.log('In else');
      return this.tokenSubject
      .filter(token => token != null)
      .take(1)
      .switchMap(token => {
        return next.handle(this.addNewAccessTokenToHeaders(request, token));
      });
    }
  }


    handle400Error(error: HttpErrorResponse) {
      if (error && error.status === 400 && error.error && error.error.error === 'invalid_grant') {
        this.injector.get(AuthService).logout();
      }

        return Observable.throw(error);
      }

    addNewAccessTokenToHeaders(req: HttpRequest<any>, token: string): HttpRequest<any> {
      console.log('Adding the access_token to the Authorization header');
      return req.clone({ setHeaders: {
        Authorization: 'Bearer ' + token
      }})
    }
  }

函数拦截必须始终返回可观察的>。你的代码有点“古怪”。我看到的主要问题是使用“do”来捕捉错误。“不”不修改请求

我有一个这样的截取(我希望代码可以帮助你)

构造函数(私有inj:Injector){
截取(req:HttpRequest,next:HttpHandler):可观察{
//如果请求具有“授权”,我们将返回请求
if(请求头有('Authorization'))
返回next.handle(req);
//我在这里得到了授权服务
const auth=this.inj.get(AuthService);
//创建httpHeaders
const httpHeaders=新的httpHeaders()
.set('Content-Type','application/json;charset=utf-8')
.set('授权',''+auth.SID)/{/{
让httpHeaders=newHttpHeaders()
.set('Content-Type','application/json;charset=utf-8')
.set('授权',''+value.SID)
const authReq=req.clone({headers:httpHeaders});
返回next.handle(authReq);
})
};
}
//另一种情况是抛出错误
返回可观察。抛出(错误);
});
}

您的函数拦截必须始终返回可观察的>。你的代码有点“古怪”。我看到的主要问题是使用“do”来捕捉错误。“不”不修改请求

我有一个这样的截取(我希望代码可以帮助你)

构造函数(私有inj:Injector){
截取(req:HttpRequest,next:HttpHandler):可观察{
//如果请求具有“授权”,我们将返回请求
if(请求头有('Authorization'))
返回next.handle(req);
//我在这里得到了授权服务
const auth=this.inj.get(AuthService);
//创建httpHeaders
const httpHeaders=新的httpHeaders()
.set('Content-Type','application/json;charset=utf-8')
.set('授权',''+auth.SID)/{/{
让httpHeaders=newHttpHeaders()
.set('Content-Type','application/json;charset=utf-8')
.set('授权',''+value.SID)
const authReq=req.clone({headers:httpHeaders});
返回next.handle(authReq);
})
};
}
//另一种情况是抛出错误
返回可观察。抛出(错误);
});
}

注意
next.handle().catch
在Angular 5和最新的RxJS库中已经更改:感谢广告@closedloop(当我编写Angular的答案时仍然使用RxJS 5),现在我们必须使用管道(catchError(err=>…),请参见
next.handle().catch
在Angular 5和最新的RxJS库中已被更改:感谢广告@closedloop(当我编写Angular的答案时仍然使用RxJS 5),现在我们必须使用管道(catchError(err=>…),请参见
ngOnInit(){
    this.getProperties();
  }

  getProperties() {
    this.propertyService.getProperties().subscribe(
      result => {
      this.properties = result;
      console.log('Received response in Properties component: ' + JSON.stringify(result));
    }, error => {
      console.log('Error receiving the properties for the view')
    },
    () => { console.log('Received the properties, now they can be displayed in the view') })
  }
constructor(private inj: Injector) { }

  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    //if the request has "Authorization" we return the request
    if (req.headers.has('Authorization'))
      return next.handle(req);

    //I get here the AuthService
    const auth = this.inj.get(AuthService);

    //create the httpHeaders
    const httpHeaders = new HttpHeaders()
      .set('Content-Type', 'application/json; charset=utf-8')
      .set('Authorization', '' + auth.SID) //<-- I use auth.SID

    const authReq = req.clone({ headers: httpHeaders });

    return next.handle(authReq).catch((err: any) => { //<--if error use a catch
      if (err instanceof HttpErrorResponse) {
        if (err.status === 401) {
          //auth.recoverSID return a Observable<{value:new SID}>
          //use switchMap to really return next.handle(authReq)
          return auth.recoverSID().switchMap((value: IResponse) => {
            let httpHeaders = new HttpHeaders()
              .set('Content-Type', 'application/json; charset=utf-8')
              .set('Authorization', '' + value.SID)

            const authReq = req.clone({ headers: httpHeaders });
            return next.handle(authReq);
          })
        };
      }
      //Other case throw an error
      return Observable.throw(err);
    });
  }