【问题标题】:Renew token before making cloned request在发出克隆请求之前更新令牌
【发布时间】:2020-10-07 02:51:44
【问题描述】:

所以我想在令牌过期时使用刷新令牌更新访问令牌,这就是我的令牌拦截器的样子:

intercept(req: HttpRequest<any>, next: HttpHandler) {
    let authservice = this.injector.get(AuthService)
      let tokenreq = req.clone({
        setHeaders: {
          Authorization: `Bearer ${authservice.setToken()}`
        }
      })
      return next.handle(tokenreq).pipe(
        catchError((error) => {
          if (error.error.message == 'The incoming token has expired') {
            authservice.renewToken()
            let tokenreq = req.clone({
              setHeaders: {
                Authorization: `Bearer ${authservice.setToken()}`
              }
            })
            return next.handle(tokenreq)
          }
          else return throwError(error)
        })
      );
    }
  }

问题是在我收到过期令牌消息后authservice.renewToken()authservice.setToken() 被同时调用,所以再次设置了过期的令牌。

另一个问题是,如果用户使用 cookie 中过期的令牌再次打开应用程序,所有GET 方法都会抛出错误并多次请求新令牌。如何处理过期令牌错误?

【问题讨论】:

  • Renewtoken 和 settoken 是同步方法吧?
  • 我想是的。我还没有声明它们是异步的
  • 你能显示renewToken的代码吗?
  • renewToken 方法到底是做什么的?

标签: angular typescript jwt jwt-auth refresh-token


【解决方案1】:

您需要传递新令牌。

return next.handle(request).pipe(
        map((event: HttpEvent<any>) => {
            return this.eventCallSendBack(event);
        }),
        catchError((error: HttpErrorResponse) => {
            if (error instanceof HttpErrorResponse) {
                switch (error.status) {
                    case 401:
                        return this.handle401Error(request, next);
                    case 403:
                        //DO Want you want here.
                        throw error;
                    default:
                        throw error;
                }
            }
            throw error;
        })
    );

这是回调函数。

private eventCallSendBack(event) {
    if (event instanceof HttpResponse) {
        return event.clone({ body: event.body });
    }
    return event;
}

您想拥有并添加令牌 fn:

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

现在假设您的令牌已过期,您需要创建 fn 以从某个 Auth 中获取新令牌。

private handle401Error(
    request: HttpRequest<any>,
    next: HttpHandler
): Observable<any> {
        return authServices
            .refreshToken(
                token //This is the old token to get the new.
            )
            .pipe(
                map(jwt => {
                    return jwt;
                }),
                switchMap((jwt: any) => {
                    return next
                        .handle(this.addToken(request, jwt.token)) // Add token in responce 
                        .pipe(
                            map((event: HttpEvent<any>) => {
                                return this.eventCallSendBack(event);
                            })
                        );
                }),
                catchError(err => {
                    return throwError(err);
                })
            );
    
}

【讨论】:

    【解决方案2】:

    您可以通过使用retryWhen 运算符将setToken 连接到返回的observable 来修复此错误行为。 这样renewTokensetToken 将不会并行执行,更重要的是setToken 将被每个请求中的拦截器链考虑在内。

    intercept(req: HttpRequest<any>, next: HttpHandler) {
        const authservice = this.injector.get(AuthService);
          
        return of(req).pipe(
              switchMap((req) => {
                return authservice.setToken()               // Gets the token. *should rename this method to getToken()
                    .pipe(
                        map(token => {                      // Set the token.
                            const tokenreq = req.clone({
                                setHeaders: {
                                    Authorization: `Bearer ${authservice.setToken()}`
                                }
                                });
    
                            return tokenreq;
                        })
                    ),
                switchMap(tokenreq => next.handle(tokenreq)), // Execute next interceptor and eventually send the request.
                retryWhen(errors => errors.pipe(
                    mergeMap((err: HttpErrorResponse, i: number) => {
                        authservice.invalidateToken()        // Invalidate token. Erase token or expires it.
                        if (error.error.message == 'The incoming token has expired') {
                            return of(err);                   // will start the current pipe all over again - and get the token once again.
                        }
    
                        return throwError(error);
                    })
                )
            )
      }
    

    说明:

    在问题中,setToken 没有连接到拦截器方法返回的可观察链。 之前的代码执行流程:

    1) interceptor method execution -> setToken() -> return observable
    2) request asked by http.get(..) -> chain of observables return by interceptors -> request sent -> chain of observables
    

    在这个答案中:

    1) interceptor method execution -> setToken() -> return observable
    2) request asked by http.get(..) -> chain of observables return by interceptors and setToken() inside one! -> request sent -> chain of observables
    

    注意:

    setToken 方法应该返回一个带有令牌的 observable,invalidateToken 应该能够删除令牌。

    这可以通过以下方式轻松实现:

    private token$: AsyncSubject<string>;
    
    getToken(): Observable {
        if (!token$) this.token$ = new AsyncSubject();
    
        getTokenOperation.subscribe(t => {
            this.token$.next(t);
            this.token$.complete();
        })
    
        return this.token$.asObservable();
    }
    
    invalidateToken() {
        this.token$ = null;
    }
    

    【讨论】:

      猜你喜欢
      • 2019-10-06
      • 2023-03-16
      • 2013-08-01
      • 2020-11-09
      • 2017-03-22
      • 2018-11-07
      • 1970-01-01
      • 2015-02-21
      • 2016-04-26
      相关资源
      最近更新 更多