首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >问答首页 >在401错误代码上没有刷新刷新令牌

在401错误代码上没有刷新刷新令牌
EN

Stack Overflow用户
提问于 2019-09-15 11:00:59
回答 1查看 3.1K关注 0票数 1

每个教程和代码片段都使用这种刷新令牌代码:https://gist.github.com/abereghici/054cefbdcd8ccd3ff03dcc4e5155242b,它在我的示例中根本无法工作,因为this.authService.refreshToken()执行HTTP请求,在.subscribe()生成之前不会执行调用。当我添加订阅调用时,它会在一个新请求中调用http post,但这在我的脑海中是杂乱无章的。你能不能解释一下,告诉我如何正确处理这件事?请不要把我见过的片段连在一起。

有关我的ASP.NET Core2.2WebAPI后端的信息:

  • /api/auth/login返回AccessToken (JWT令牌+刷新令牌字符串)。JWT令牌仅为测试目的在1分钟内过期。刷新令牌在10分钟内过期。
  • /api/auth/token/refresh返回一个新的AccessToken。-- JWT和Refresh令牌都是重新创建的。另一件重要的事情是,如果刷新令牌过期并在此之后发出刷新请求,它将返回一个BadRequest,该应该在角度部分处理,可能在管道中有一个catchError()
  • /api/auth/logout将撤销刷新令牌。因此,基本上,在this.authService.logout()上也会有http请求,即使它不在我下面提供的代码中。

auth.interceptor.ts

代码语言:javascript
代码运行次数:0
运行
复制
import { Injectable } from '@angular/core';
import {
  HttpEvent,
  HttpInterceptor,
  HttpHandler,
  HttpRequest,
  HttpErrorResponse
} from '@angular/common/http';
import { Observable, throwError, BehaviorSubject } from 'rxjs';
import { catchError, switchMap } from 'rxjs/operators';

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

@Injectable()
export class AuthInterceptor implements HttpInterceptor {
  private refreshTokenInProgress = false;
  private tokenSubject = new BehaviorSubject<any>(null);

  constructor(private authService: AuthService) { }

  intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    return next.handle(request)
      .pipe(
        catchError((error: HttpErrorResponse) => {

          if (error instanceof HttpErrorResponse) {
            if (error.status === 401) {
              return this.handleHttpResponseError(request, next);
            }
          }

          return throwError(error);
        })
      );
  }

  private handleHttpResponseError(request: HttpRequest<any>, next: HttpHandler) {
    const accessToken = this.authService.getJwtToken();

    // if jwt token is not set, we just let the request execute
    if (!accessToken) {
      return next.handle(request);
    }

    // if jwt token has not expired yet, we add the authorize header
    // otherwise we refresh the token
    if (!this.authService.isTokenExpired()) {
      console.log('added authorization to the headers')

      return next.handle(this.attachTokenToRequest(request, accessToken));
    } else {
      console.log('token expired');

      this.authService.refreshToken()
        .pipe(
          switchMap(result => {
            console.log(`API returned: ${result}`);

            return next.handle(this.attachTokenToRequest(request, accessToken));
          })
        );

      return next.handle(request);
    }
  }

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

auth.service.ts

代码语言:javascript
代码运行次数:0
运行
复制
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { environment } from 'src/environments/environment';
import { map } from 'rxjs/operators';
import { JwtHelperService } from '@auth0/angular-jwt';

import { AccessToken } from '../types/user';

@Injectable()
export class AuthService {
  private actionUrl: string;
  private readonly JWT_TOKEN = 'access_token';
  private readonly REFRESH_TOKEN = 'refresh_token';

  constructor(private httpClient: HttpClient) {
    this.actionUrl = `${environment.baseUrls.server}api/auth`;
  }

  isLoggedIn(): boolean {
    return !!this.getJwtToken();
  }

  login(credentials: { email: string, password: string }) {
    return this.httpClient.post<AccessToken>(`${this.actionUrl}/login`, credentials)
      .pipe(
        map(tokens => {
          this.setJwtToken(tokens.accessToken);
          this.setRefreshToken(tokens.refreshToken);
          return true;
        })
      );
  }

  refreshToken() {
    console.log('refreshToken() called');

    const helper = new JwtHelperService();
    const email = helper.decodeToken(this.getJwtToken()).sub;

    return this.httpClient.post<AccessToken>(`${this.actionUrl}/token/refresh`, { 'refreshToken': localStorage.getItem(this.REFRESH_TOKEN), 'email': email })
      .pipe(
        map(tokens => {
          console.log('refreshToken().pipe called()');

          this.setJwtToken(tokens.accessToken);
          this.setRefreshToken(tokens.refreshToken);
          return true;
        })
      );
  }

  logout() {
    localStorage.removeItem(this.JWT_TOKEN);
    localStorage.removeItem(this.REFRESH_TOKEN);
  }

  isTokenExpired(): boolean {
    const helper = new JwtHelperService();
    return helper.isTokenExpired(this.getJwtToken());
  }

  getJwtToken(): string {
    return localStorage.getItem(this.JWT_TOKEN);
  }

  setJwtToken(token: string) {
    localStorage.setItem(this.JWT_TOKEN, token);
  }

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

  setRefreshToken(token: string) {
    localStorage.setItem(this.REFRESH_TOKEN, token);
  }
}
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2019-09-15 12:40:44

我认为问题就在这里:

代码语言:javascript
代码运行次数:0
运行
复制
else {
 console.log('token expired');

  // Here!!!
  this.authService.refreshToken()
    .pipe(
      switchMap(result => {
        console.log(`API returned: ${result}`);

        return next.handle(this.attachTokenToRequest(request, accessToken));
      })
    );

  return next.handle(request);
 }
}

返回从authService.refreshToken.返回的可观察到的,而不是

您必须确保返回它,以便使其成为main stream的一部分,这意味着如果一个观察者订阅了,那么main stream也会被订阅。

我创建了一个StackBlitz实例来说明如何在一个流中包含多个可观察到的内容,以及当订阅“主”流时它们都是如何被订阅的。

因此,我认为(并希望)这将解决你的问题:

代码语言:javascript
代码运行次数:0
运行
复制
else {
 console.log('token expired');

  return this.authService.refreshToken()
    .pipe(
      switchMap(result => {
        console.log(`API returned: ${result}`);

        return next.handle(this.attachTokenToRequest(request, accessToken));
      })
    );
 }
}
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/57943479

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档