簡體   English   中英

攔截器Angular 6中的刷新令牌(JWT)

[英]Refresh token (JWT) in interceptor Angular 6

最初,我有一個簡單地檢查令牌是否存在的函數,如果不存在,則將用戶發送到登錄標頭。 現在我需要在刷新令牌的幫助下實現令牌過期時刷新的邏輯。 但是我得到一個錯誤401。刷新功能沒有時間工作,攔截器中的工作進一步導致錯誤。 如何修復代碼,以便等待刷新完成、獲取新令牌而不重定向到登錄頁面?

令牌攔截器

import {HttpErrorResponse, HttpEvent, HttpHandler, HttpInterceptor, HttpRequest} from "@angular/common/http";
import {Injectable, Injector} from "@angular/core";
import {AuthService} from "../services/auth.service";
import {Observable, throwError} from "rxjs";
import {catchError, tap} from "rxjs/operators";
import {Router} from "@angular/router";
import {JwtHelperService} from "@auth0/angular-jwt";

@Injectable({
  providedIn: 'root'
})
export class TokenInterceptor implements HttpInterceptor{

  private auth: AuthService;

  constructor(private injector: Injector, private router: Router) {}

  jwtHelper: JwtHelperService = new JwtHelperService();

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

    this.auth = this.injector.get(AuthService);

    const accToken = this.auth.getToken();
    const refToken = this.auth.getRefreshToken();

    if ( accToken && refToken ) {

      if ( this.jwtHelper.isTokenExpired(accToken) ) {
        this.auth.refreshTokens().pipe(
          tap(
            () => {
              req = req.clone({
                setHeaders: {
                  Authorization: `Bearer ${accToken}`
                }
              });
            }
          )
        )
      } else {
        req = req.clone({
          setHeaders: {
            Authorization: `Bearer ${accToken}`
          }
        });
      }

    }
    return next.handle(req).pipe(
      catchError(
        (error: HttpErrorResponse) => this.handleAuthError(error)
      )
    );
  }

  private handleAuthError(error: HttpErrorResponse): Observable<any>{
    if (error.status === 401) {
      this.router.navigate(['/login'], {
        queryParams: {
          sessionFailed: true
        }
      });
    }
    return throwError(error);
  }

}

身份驗證服務

import {Injectable} from "@angular/core";
import {HttpClient, HttpHeaders} from "@angular/common/http";
import {Observable, of} from "rxjs";
import {RefreshTokens, Tokens, User} from "../interfaces";
import {map, tap} from "rxjs/operators";

@Injectable({
  providedIn: 'root'
})
export class AuthService{

  private authToken = null;
  private refreshToken = null;

  constructor(private http: HttpClient) {}

  setToken(authToken: string) {
    this.authToken = authToken;
  }

  setRefreshToken(refreshToken: string) {
    this.refreshToken = refreshToken;
  }

  getToken(): string {
    this.authToken = localStorage.getItem('auth-token');
    return this.authToken;
  };

  getRefreshToken(): string {
    this.refreshToken = localStorage.getItem('refresh-token');
    return this.refreshToken;
  };

  isAuthenticated(): boolean {
    return !!this.authToken;
  }

  isRefreshToken(): boolean {
    return !!this.refreshToken;
  }

  refreshTokens(): Observable<any> {

    const httpOptions = {
      headers: new HttpHeaders({
        'Authorization': 'Bearer ' + this.getRefreshToken()
      })
    };

    return this.http.post<RefreshTokens>('/api2/auth/refresh', {}, httpOptions)
      .pipe(
        tap((tokens: RefreshTokens) => {
          localStorage.setItem('auth-token', tokens.access_token);
          localStorage.setItem('refresh-token', tokens.refresh_token);
          this.setToken(tokens.access_token);
          this.setRefreshToken(tokens.refresh_token);
          console.log('Refresh token ok');
        })
      );
  }

}

在您的示例中,您從不訂閱您的refreshTokens().pipe()代碼。 如果沒有訂閱,observable 將不會執行。

你必須做這樣的事情:

const firstReq = cloneAndAddHeaders(req);

return next.handle(firstReq).pipe(
   catchError(
      err => {
         if (err instanceof HttpErrorResponse) {
            if (err.status === 401 || err.status === 403) {
               if (firstReq.url === '/api2/auth/refresh') {
                  auth.setToken('');
                  auth.setRefreshToken('');
                  this.router.navigate(['/login']);
               } else {
                  return this.auth.refreshTokens()
                    .pipe(mergeMap(() => next.handle(cloneAndAddHeaders(req))));
               }
            }
            return throwError(err.message || 'Server error');
         }
      }
    )
 );

cloneAndAddHeaders的實現應該是這樣的:

private cloneAndAddHeaders(request: HttpRequest<any>): HttpRequest<any> {
    return request.clone({
       setHeaders: {
           Authorization: `YourToken`
       }
    });
}
req = this.auth.refreshTokens().pipe(
      switchMap(() => req.clone({
            setHeaders: {
              Authorization: `Bearer ${this.auth.getToken()}`
            }
          }))
      )

這將首先調用refreshToken並在那里運行tap ,然后使用新的this.auth.getToken()發出請求,請注意accToken仍然具有舊值,因為代碼不會重新運行。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM