Angular JWT 拦截器切换承载令牌以进行刷新

Posted

技术标签:

【中文标题】Angular JWT 拦截器切换承载令牌以进行刷新【英文标题】:Angular JWT interceptor switch bearer token for refresh 【发布时间】:2022-01-13 00:41:53 【问题描述】:

我已经实现了 JWT 和刷新令牌流。过去我实现这个的时候,我做的有点不同,主要是刷新令牌是在正文中发送的。

但是现在我做了不同的事情,我必须通过 Authorization 标头发送访问令牌,但是我的拦截器代码不想切换不记名令牌。我该如何解决,如果我想刷新,我实际上使用刷新令牌作为承载令牌而不是已过期的访问令牌?

intercept(
    request: HttpRequest<any>,
    next: HttpHandler
  ): Observable<HttpEvent<any>> 
    const token = this.userService.getJWTToken();
    if (token) 
      request = this.addToken(request, token);
    

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

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

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

      //This is what I've tried, to switch out the tokens
      request = this.addToken(request, this.userService.getRefreshToken());

      //this.userService.refreshToken() is a POST request, where I want the refresh token as the bearer token, instead of the access token
      return this.userService.refreshToken().pipe(
        switchMap((token: TokenDTO) => 
          this.isRefreshing = false;
          this.refreshTokenSubject.next(token.accessToken);
          return next.handle(this.addToken(request, token.accessToken));
        )
      );
     else 
      return this.refreshTokenSubject.pipe(
        filter((token) => token != null),
        take(1),
        switchMap((accessToken) =>
          next.handle(this.addToken(request, accessToken))
        )
      );
    
  

我还尝试在发布请求中将 HTTP 标头设置为授权持有者令牌

public refreshToken(): Observable<TokenDTO> 
    const headers = new HttpHeaders(
      'Content-Type': 'application/json',
      Authorization: `Bearer $this.getRefreshToken()`,
    );

    return this.httpClient
      .post<TokenDTO>(`$this.hostname/users/refreshToken`, , headers)
      .pipe(
        tap((tokens: TokenDTO) => 
          this.saveTokens(tokens);
        )
      );
  

【问题讨论】:

【参考方案1】:

您不应使用不记名令牌替换访问令牌来刷新它,而是使用您的刷新令牌调用专用令牌端点来获取新的访问令牌。有时,根据设置,您可能还会取回新的刷新令牌。

【讨论】:

嗯,这正是我想要做的,也许我措辞不好。我打算使用刷新令牌向刷新令牌端点发出请求,从那里我得到一个新的访问令牌和一个新的刷新令牌。然后我保存新的访问令牌并使用它。或者你是说我之前的项目中的实现,我在正文中添加了刷新令牌是正确的? 很难理解到底是什么问题 :-) 我的意思是你过去的项目听起来有点不对劲。刷新令牌不应作为请求中的授权标头发送,也不应发送到 idp 以外的任何其他服务。 那么,实际发送和刷新令牌的最佳方式是什么? 当今一个流行的选择是使用 BFF 模式来管理令牌,这也比尝试在 Angular 中管理令牌提供更好的安全性。 blog.bitsrc.io/…【参考方案2】:

您不应在 Authorization 标头中使用刷新令牌。这意味着您不应仅根据刷新令牌的值来授权请求​​。如果您这样做,那么任何窃取刷新令牌的人都可以使用它来获取新的访问令牌!刷新令牌是发布新令牌所需的数据,但您应该以另一种方式授权客户端(您的应用程序)。例如。您可以使用 HTTP Basic 身份验证并传递客户端的 ID 和密钥,或者使用更复杂的东西,例如双向 TLS。

【讨论】:

以上是关于Angular JWT 拦截器切换承载令牌以进行刷新的主要内容,如果未能解决你的问题,请参考以下文章

Angular 6 - 为啥生产构建中缺少承载令牌? (在开发版本中工作正常)

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

Angular 6在令牌更新后重制请求之前更改JWT令牌

JWT 令牌失效的未知原因

浏览器中直接 URL 命中的 Jwt 令牌身份验证

用于授权和身份验证的多个 JWT 承载