【问题标题】:Angular Auth interceptor failing to refresh token - Angular 7Angular Auth拦截器无法刷新令牌 - Angular 7
【发布时间】:2019-09-03 01:01:27
【问题描述】:

如果刷新令牌过期,我会尝试刷新访问令牌。登录后,我收到了两个令牌并将它们存储在我的本地存储中。在服务器以状态 401 响应后,我尝试发送刷新令牌但它失败了,似乎拦截器在标头内发送 berer 访问令牌而不是刷新令牌

身份验证拦截器

@Injectable()
export class AuthInterceptor implements HttpInterceptor {

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

  constructor(private authService: LoginService,
              private uploadService: ContractUploadService  ) { }


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

    if (this.authService.getJwtAccessToken()) {
      request = this.addToken(request, this.authService.getJwtAccessToken());
    }

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

  addToken(request: HttpRequest<any>, token: string) {
    return request.clone({
      setHeaders: {
        'Authorization': `Bearer ${token}`
      }
    });
  }

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

      return this.authService.refreshToken().pipe(
        finalize(() => this.isRefreshing = false),
        switchMap((token: any) => {
          if (token) {
            this.refreshTokenSubject.next(token.jwt);
            return next.handle(this.addToken(request, token.jwt));
          }
          this.authService.doLogoutUser();
          this.uploadService.stopUploadStatusChecker();
        }),
        catchError(error => {
          this.authService.doLogoutUser();
          this.uploadService.stopUploadStatusChecker();
          return throwError(error);
        })
      );
    } else {
      return this.refreshTokenSubject.pipe(
        filter(token => token != null),
        take(1),
        switchMap(jwt => {
          return next.handle(this.addToken(request, jwt));
        }));
    }
  }
}

刷新令牌请求

  refreshToken() {
    return this.http
      .post<any>(BACK_END_URL_REFRESH, {
        'refresh_token': this.getRefreshToken()
      })
      .pipe(
        tap((tokens: any) => {
          this.storeJwtToken(tokens.jwt);
          this.isAuthenticated = true;
        }),
        catchError((err: any) => {
          console.log(err)
          this.doLogoutUser();
          return throwError(err);
        })
      );
  }

登录

  login(user: UserLogin) {
    const userData = new FormData();
    userData.append('user', user.name);
    userData.append('pwd', user.password);

    this.http.post<{ access_token: string; refresh_token: string }>(BACK_END_URL, userData).pipe(
      finalize(() => this.interactionService.setSpinnerStatus.next(false)),
    )
      .subscribe(
        response => {
          if (response.access_token) {
            this.isAuthenticated = true;
            this.interactionService.setSnackBar('User logged in successfully', 'success');
            this.storeTokens(response.refresh_token, response.access_token);
            this.extractUserInfoFromToken(response.access_token);
            this.router.navigate(['/user-panel']);
          }
        },
        error => {
          this.interactionService.setSnackBar('Wrong user name or password', 'error');
        }
      );
  }

本地存储方法

  private getRefreshToken() {
    return localStorage.getItem(this.REFRESH_TOKEN);
  }

  private storeJwtToken(jwt: string) {
    localStorage.setItem(this.JWT_ACCESS_TOKEN, jwt);
  }

  private storeTokens(refreshToken, accessToken) {
    localStorage.setItem(this.REFRESH_TOKEN, refreshToken);
    localStorage.setItem(this.JWT_ACCESS_TOKEN, accessToken);
  }

  private removeTokens() {
    localStorage.removeItem(this.JWT_ACCESS_TOKEN);
    localStorage.removeItem(this.REFRESH_TOKEN);
  }

[编辑] 尝试实施@Abdellah ASKI 建议,但仍然无法正常工作

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

    if (this.authService.getJwtAccessToken() && request.headers.get('No-Auth') !== 'true') {
      request = this.addToken(request, this.authService.getJwtAccessToken());
    }

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

 refreshToken() {
    return this.http
      .post<any>(BACK_END_URL_REFRESH, {
        refresh_token: this.getRefreshToken(),
        'No-Auth': 'true'
      })
      .pipe(
          tap((tokens: any) => {
            this.storeJwtToken(tokens.jwt);
            this.isAuthenticated = true;
          }),
            catchError((err: any) => {
              this.doLogoutUser();
              return throwError(err);
            })
      );

[编辑2]

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

    if (this.authService.getJwtAccessToken() && !request.headers.has('No-Auth')) {
      request = this.addToken(request, this.authService.getJwtAccessToken());
    }

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

  addToken(request: HttpRequest<any>, token: string) {
    return request.clone({
      setHeaders: {
        'Authorization': `Bearer ${token}`
      }
    });
  }

Headers

【问题讨论】:

    标签: angular


    【解决方案1】:

    [更新]

    解决这个问题的想法是在每个请求中添加一个名为 No-Auth:true 的标头,而您不知道要使用什么来发送访问令牌。

    所以在你的代码中你需要这样做:

    在您的 refreshToken() 函数中,您需要像这样添加一个新标头:

    refreshToken() {
        return this.http
          .post<any>(BACK_END_URL_REFRESH, {
            'refresh_token': this.getRefreshToken(),
            'No-Auth': 'true' <<-------<<-------<<-------<<-------
          })
          .pipe(
            ....
          );
      }
    

    并且在您的 Auth 拦截器上,您需要检查此标头是否存在,您不需要添加访问令牌:

    intercept(request: HttpRequest<any>, next: HttpHandler): Observable<any> {
        if (request.headers.has('No-Auth')) { <<-------<<-------<<-------
            return next.handle(request);
        }
    
        if (this.authService.getJwtAccessToken()) {
          request = this.addToken(request, this.authService.getJwtAccessToken());
        }
    
        ....
      }
    

    【讨论】:

    • 我做到了,但仍然无法正常工作。我添加了代码
    • 你能更新源代码,让我看看你做了什么吗?如果有错误,也很高兴提及。
    • 非常感谢您抽出宝贵的时间。我添加了标题的更改和图像
    • 我的错,在我给你的条件下可能是个问题,它应该使用has() 而不是使用get() 获取标头而不是测试(如果标头不存在,它将return null 这就是当前代码不起作用的原因)。您可以尝试将request.headers.get('No-Auth') != 'true' 替换为!request.headers.has('No-Auth')我会相应地更新答案。
    • 我更改了它,它仍然发送它(我编辑了代码)
    猜你喜欢
    • 1970-01-01
    • 2017-12-31
    • 2017-12-26
    • 2018-06-12
    • 1970-01-01
    • 2019-03-28
    • 2019-10-22
    • 1970-01-01
    • 2017-12-25
    相关资源
    最近更新 更多