Angular 9 中用于刷新 jwt 令牌的 HttpInterceptor 问题

Posted

技术标签:

【中文标题】Angular 9 中用于刷新 jwt 令牌的 HttpInterceptor 问题【英文标题】:Problems with HttpInterceptor for jwt token refreshing in Angular 9 【发布时间】:2020-08-25 19:41:09 【问题描述】:

我正在使用 jwt 进行身份验证。我在提神方面遇到了麻烦。可能我认为这是由于我在 rxjs 可观察对象和操作符方面缺乏经验。我使用 auth.service.ts

的这些方法从后端获取刷新令牌
    getNewAccessToken(refreshToken: string)
        return this.httpClient.post<Token>(`$this.baseService.baseUrlauth-token-refresh/`,  refresh: refreshToken , this.baseService.httpOptions).pipe(
          map((response:Token) => 
            this.cookieService.set(environment.tokenAccessName, response.access, null, '/', null, null, 'Strict');
            return response;
          
        ));
      

      getAuthToken()  
        return this.cookieService.get(this.tokenAccessName);
      

      getRefreshToken()  
        return this.cookieService.get(this.tokenRefreshName);
      

      hasValidToken() 
        if (this.getAuthToken())
          return !helper.isTokenExpired(this.getAuthToken());
        else 
          return false;
      

这是我的拦截器auth-interceptor.ts

    import  Injectable  from '@angular/core';
import 
    HttpRequest,
    HttpHandler,
    HttpEvent,
    HttpInterceptor,
    HttpErrorResponse
   from '@angular/common/http';

import  AuthService  from '../services/auth.service';

import  throwError  from 'rxjs';
import  catchError, map  from 'rxjs/operators';
import  Token  from '../models/token';

@Injectable()
export class AuthInterceptor implements HttpInterceptor 

  constructor(private authService: AuthService) 

  intercept(request: HttpRequest<any>, next: HttpHandler) 
    const authToken = this.authService.getAuthToken();
    if (authToken) 
      if (this.authService.hasValidToken()) 
        request = this.addAuthenticationToken(request);
       
     

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

  handle401Error(request: HttpRequest<any>, next: HttpHandler) 
    const refreshToken = this.authService.getRefreshToken();
    this.authService.getNewAccessToken(refreshToken).subscribe(
      (response: Token) => 
        this.addAuthenticationToken(request);
        return next.handle(request);
      ,
      error => throwError(error)
    )
  

  addAuthenticationToken(request: HttpRequest<any>) 
    const authToken = this.authService.getAuthToken();
    return request.clone(
      headers: request.headers.set('Authorization', 'Bearer ' + authToken),
    );
  


我的ide显示如下错误

Property 'intercept' in type 'AuthInterceptor' is not assignable to the same property in base type 'HttpInterceptor'.
Type '(request: HttpRequest<any>, next: HttpHandler) => Observable<unknown>' is not assignable to type '(req: HttpRequest<any>, next: HttpHandler) => Observable<HttpEvent<any>>'.
Type 'Observable<unknown>' is not assignable to type 'Observable<HttpEvent<any>>'.
Type 'unknown' is not assignable to type 'HttpEvent<any>'.
Property 'type' is missing in type '' but required in type 'HttpUserEvent<any>'.

还有

Argument of type '(error: any) => void | Observable<never>' is not assignable to parameter of type '(err: any, caught: Observable<HttpEvent<any>>) => ObservableInput<any>'.
Type 'void | Observable<never>' is not assignable to type 'ObservableInput<any>'.
Type 'void' is not assignable to type 'ObservableInput<any>'.

我想我可能弄错了返回类型,但我不知道出了什么问题

【问题讨论】:

我们可以得到你的 auth-interceptor 的内容吗? @htn 谢谢,我没有注意到我粘贴了错误的代码 我了解您的意图。很容易找出问题所在:handle401Error 应该返回一个Observable(此处为空)。第一个return throwError(error) 应替换为throw error;。对于第二个,订阅的 errorHandler 中不要返回任何内容。但是,要给您正确的答案并不容易:当出现 401 错误时,请尝试在重播请求之前刷新令牌(如果您没有正确执行,您可能会陷入无限循环) 抛出错误; 不会改变任何东西。我不断收到同样的错误。说到401响应时刷新token的策略,看了很多教程解释,觉得是对的 这是一种正确的做法,但重试很复杂。一个更简单的处理方法是:本地检查令牌有效性,如果需要刷新,并在收到 401 时注销。好吧,我重新阅读了您的代码:我们不会在收到 401 时发送 refreshToken,但您使用它来生成另一个令牌...我建议您找到一个可行的示例。 【参考方案1】:

我终于找到了解决办法。无论如何,有人可以尝试改进它 我做了以下更改,现在它可以工作了 将 Observable> 添加到 intercept 签名导致我的问题中解释的第一个问题消失了。对于第二个,我遵循问题​​评论中的建议。 拦截器类

@Injectable()
export class AuthInterceptor implements HttpInterceptor 

  constructor(private authService: AuthService) 

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

    if (this.authService.hasValidToken()) 
      request = this.addAuthenticationToken(request);
     

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

  handle401Error(request: HttpRequest<any>, next: HttpHandler) 
    return this.authService.getNewAccessToken().pipe(
      switchMap((response: Token) => 
        return next.handle(this.addAuthenticationToken(request));
      )
    )
  

  addAuthenticationToken(request: HttpRequest<any>) 
    const authToken = this.authService.getAuthToken();
    return request.clone(
      setHeaders: 
       'Authorization': `Bearer $authToken` 
      
    );
  


虽然我的身份验证服务中用于获取刷新令牌的功能已被修改为

getNewAccessToken()
    return this.httpClient.post<Token>(`$this.baseService.baseUrlauth-token-refresh/`,  refresh: this.getRefreshToken() , this.baseService.httpOptions).pipe(
      tap((response:Token) => 
        this.cookieService.set(environment.tokenAccessName, response.access, null, '/', null, null, 'Strict');
      
    ));
  

【讨论】:

以上是关于Angular 9 中用于刷新 jwt 令牌的 HttpInterceptor 问题的主要内容,如果未能解决你的问题,请参考以下文章

使用 Angular 和 JWT 令牌持续登录

angular 2 http异常处理程序和jwt刷新

JWT 刷新令牌 Angular

使用带有 JWT Auth/Laravel 的 Angular JS 获取新的刷新令牌后,如何恢复/重新发送请求?

过期后如何刷新 JWT 令牌(Angular 1.5 + Laravel 5.2)

Angular 4.3 - HTTP 拦截器 - 刷新 JWT 令牌