代码之家  ›  专栏  ›  技术社区  ›  ilinieja

Angular 5 HTTPInterceptor请求前刷新令牌

  •  1
  • ilinieja  · 技术社区  · 6 年前

    我需要在发出请求之前刷新httpinterceptor中的令牌,为此,我在请求之前检查访问令牌,如果令牌过期,则调用refresh。 目前,我的拦截器如下所示:

    @Injectable()
    export class TokenInterceptor implements HttpInterceptor {
    
      private refreshTokenSubject = new BehaviorSubject(null);
      private refreshTokenObservable = this.refreshTokenSubject.asObservable();
      private isRefreshingToken = false;
    
      constructor(private authService: AuthService) {
      }
    
      intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
        const tokenData = AuthService.getCurrentSessionData();
    
        if (!this.isRefreshingToken) {
          // if no token set - make request as-is
          const tokenSet = tokenData && tokenData.token;
          if (!tokenSet) {
            return next.handle(request);
          }
    
          // proceed if token not expired
          const tokenExpired = new Date(tokenData.expirationDate) < new Date();
          if (!tokenExpired) {
            return next.handle(this.setAuthHeader(request, tokenData.token));
          }
    
          // check if we can refresh the token and logout instantly if not
          const tokenRefreshable = tokenData.refreshToken && new Date(tokenData.refreshTokenExpirationDate) > new Date();
          if (!tokenRefreshable) {
            this.authService.logout();
            return Observable.throw('');
          }
    
          this.isRefreshingToken = true;
    
          // make all subsequent requests wait for new token
          this.refreshTokenSubject.next(null);
    
          // make refresh request
          return this.authService.refreshToken()
            .switchMap((res: any) => {
              AuthService.storeSessionData(res, Utils.getLocalStorageItem(STORAGE_KEYS.STAY_LOGGED_IN));
              this.isRefreshingToken = false;
    
              // let subsequent awaiting proceed
              this.refreshTokenSubject.next(res.access_token);
              return next.handle(this.setAuthHeader(request, res.access_token));
            })
            .catch((err) => {
              this.authService.logout();
              return Observable.throw('');
            })
            .finally(() => {
              this.isRefreshingToken = false;
            });
        } else {
          // if token refreshing in progress - wait for new token
          return this.refreshTokenObservable
            .filter(token => token !== null)
            .take(1)
            .switchMap((token) => {
              return next.handle(this.setAuthHeader(request, token));
            });
        }
      }
    
      private setAuthHeader(request, token) {
        return request.clone({
          setHeaders: {
            Authorization: `Bearer ${token}`,
          },
        });
      }
    }
    

    问题是 this.authService.refreshToken() 从不发出请求,后续请求也从不继续。我想这是因为没有订阅httpclient返回的observable,这里是 refreshToken 方法代码:

    public refreshToken() {
        const tokenData = AuthService.getCurrentSessionData();
    
        return this.http.post(
          `${environment.apiPath}/auth/refresh`,
          { refresh_token: tokenData.refreshToken },
        );
    }
    

    我该怎么修改这个代码 刷新令牌 请求并让其他请求按预期进行?

    1 回复  |  直到 6 年前
        1
  •  1
  •   Ashish Ranjan    6 年前
     @Injectable()
        export class RequestInterceptorService implements HttpInterceptor {
    
          @BlockUI() blockUI: NgBlockUI;
          isRefreshingToken = false;
          tokenSubject: BehaviorSubject<string> = new BehaviorSubject<string>(null);
          oldToken = localStorage.getItem('access_token');
          constructor(private authService: AuthService, private localStorageToken: AppLocalStorage,
                      private route: ActivatedRoute, private router: Router) {}
    
          addToken(req: HttpRequest<any>, token: string): HttpRequest<any> {
            return req.clone({ setHeaders: { Authorization: 'Bearer ' + token, 'Access-Control-Allow-Origin': '*' }});
          }
    
          intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpSentEvent | HttpHeaderResponse
            | HttpProgressEvent | HttpResponse<any> | HttpUserEvent<any>> {
            return next.handle(this.addToken(req, this.authService.getAuthToken()))
              .catch(error => {
                if (error instanceof HttpErrorResponse) {
                  switch ((<HttpErrorResponse>error).status) {
                    case 400:
                      return this.handle400Error(error);
                    case 401: this.authService.refresh().subscribe((data) => { // A call has been made at an instant where network get 401 status code,It will prevent the asynchronous way of handling network calls.
                        this.localStorageToken.setRefreshTokens(data); //localstorageToken is used to store all the response, including access token, expiry time and refresh token to get stored in localstorage.
                      }
                    );
                      return this.handle401Error(req, next);
                    // default: this.logoutUser();
                  }
                } else {
                  return Observable.throw(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.
              // console.log('Bad Error');
              return this.logoutUser();
            }
    
            return Observable.throw(error);
          }
    
          handle401Error(req: HttpRequest<any>, next: HttpHandler) {
            if (!this.isRefreshingToken) {
              this.isRefreshingToken = true;
    
              // Reset here so that the following requests wait until the token
              // comes back from the refreshToken call.
              this.tokenSubject.next(null);
    
              return this.authService.refreshToken()
                .switchMap((newToken: string) => {
                  newToken = localStorage.getItem('access_token');
                  if (newToken) {
                    this.tokenSubject.next(newToken);
                    if (this.oldToken === newToken) {
                      return this.logoutUser();
                    } else {
                      return next.handle(this.addToken(req, newToken));
                    }
                  }
    
                  // If we don't get a new token, we are in trouble so logout.
                  return this.logoutUser();
                })
                .catch(error => {
                  // If there is an exception calling 'refreshToken', bad news so logout.
                  return this.logoutUser();
                })
                .finally(() => {
                  this.isRefreshingToken = false;
                });
            } else {
              return this.tokenSubject
                .filter(token => token != null)
                .take(1)
                .switchMap(token => {
                  return next.handle(this.addToken(req, token));
                });
            }
          }
    
          logoutUser() {
            // Route to the login page (implementation up to you)
            this.router.navigate(['login']).then(() => { this.blockUI.stop(); });
            return Observable.throw('');
          }
        }
    

    现在authservice用于获取刷新令牌和登录, 每当需要刷新令牌时都会调用此服务,我为获取刷新令牌留出了2秒的时间间隔。

        export class AuthService {
          private TokenApi = AppSettings.DEVELOPMENT_API;
          private newToken = ' ';
          private current_token: string;
          private refresh_token: string = localStorage.getItem('refresh_token');
    
          constructor(private http: HttpClient, private localStorageToken: AppLocalStorage) {
          }
    
          login(username: string, password: string): Observable<TokenParams> {
            const headers = new HttpHeaders({'content-type': 'application/x-www-form-urlencoded'});
            const loginApi = this.TokenApi + '/oauth/token?username=' + username + '&password=' + password + '&grant_' +
              'type=password....';
            return this.http.post<TokenParams>(loginApi, '', {headers: headers});
          }
    
          refresh(): Observable<TokenParams> {
            this.refresh_token = localStorage.getItem('refresh_token');
            const refreshToken = this.TokenApi + '/oauth/token?refresh_token=' + this.refresh_token + '&grant_' +
              'type=refresh_token...';
            return this.http.post<TokenParams>(refreshToken, '' );
          }
    
          logout() {
            this.localStorageToken.emptyLocalStorage();
          }
    
          getAuthToken() {
            this.current_token = localStorage.getItem('access_token');
            return this.current_token;
          }
    
          refreshToken(): Observable<string> {
           this.newToken = localStorage.getItem('access_token');
            this.current_token = this.newToken;
            return Observable.of(localStorage.getItem('access_token')).delay(2000);
          }
    
        }