首页 文章

Angular 4 JWT Http Interceptor不适用于链请求

提问于
浏览
0

我已经为新的HttpClient实现了http拦截器,一切正常,令牌被刷新为单个请求,但如果我尝试访问从两个api延迟加载数据的路由,我收到错误,我的JWT令牌被列入黑名单 .

Laravel Backend Token Refresh Method:

public function refreshToken() {

        $token = \JWTAuth::getToken();

        if (! $token) {
            return response()->json(["error" => 'Token is invalid'], 401);
        }

        try {

            $refreshedToken = \JWTAuth::refresh($token);
            $user = \JWTAuth::setToken($refreshedToken)->toUser();

        } catch (JWTException $e) {

            return response()->json(["error" => $e->getMessage()], 400);
        }

        return response()->json(["token" => $refreshedToken, "user" => $user], 200);
    }

Angular Http Interceptor:

@Injectable()
export class RefreshTokenInterceptor implements HttpInterceptor {

    constructor(private injector: Injector) { }

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

        return next.handle(request).catch((errorResponse: HttpErrorResponse) => {

            const error = (typeof errorResponse.error !== 'object') ? JSON.parse(errorResponse.error) : errorResponse;

            if(errorResponse.status === 401 && error.error === 'token_expired') {

                const http = this.injector.get(HttpClient);

                let token = localStorage.getItem('token');

                return http.post<any>(`${environment.apiBaseUrl}token/refresh`, {},
                    {headers: new HttpHeaders().set('Authorization', `Bearer ${token}`)})
                    .flatMap(data => {

                        localStorage.setItem('currentUser', JSON.stringify(data));
                        localStorage.setItem('token', data.token);

                        const cloneRequest = request.clone({setHeaders: {'Authorization': `Bearer ${data.token}`}});
                        return next.handle(cloneRequest);
                    });
            }

            return Observable.throw(errorResponse);
        });
    }
}

我使用解析器的路线:

{
        path: '',
        children: [ {
            path: 'create',
            component: CreateCarComponent,
            resolve: {
                subcategories: SubCategoriesResolver,
                companies: CompaniesResolver
            }
        }]
    }

Companies Resolver: (汽车解析器与此类似)

@Injectable()
export class CompaniesResolver implements Resolve<any> {

    constructor(private _userService: UserService) {}

    resolve(route: ActivatedRouteSnapshot) {
        return this._userService.getCompaniesList();
    }
}

User Service Method Example:

getUserCardsApi: string = "user/cars/all";

    getCardsList() :  Observable<any[]> {

        return this._http.get(environment.apiBaseUrl + this.getUserCardsApi, this.jwtHeaders())
            .catch(error => {

                return Observable.throw(error);
            });
    }

Headers:

private jwtHeaders() {

        let currentUser = JSON.parse(localStorage.getItem('currentUser'));

        return {headers: new HttpHeaders().set('Authorization', 'Bearer ' + currentUser.token)}
        }
    }

每当我点击超过2个解析器的路由时,我收到的第一个响应是正确的,并返回带有用户对象的刷新令牌,然后立即返回下一个令牌黑名单 . 你能否说一下可能出现的问题,我花了太多时间来解决这个问题(

更新1:

我注意到第二个刷新请求是传递旧令牌而不是新令牌,这就是为什么Laravel将令牌列入黑名单的原因

3 回答

  • 0

    注入 Headers 的方式有些“奇怪”,请尝试:

    let httpHeaders = new HttpHeaders()
                  .set('Authorization', `Bearer ${data.token}`)
                const cloneRequest = request.clone({ headers: httpHeaders });
                return next.handle(cloneRequest );
    
  • 0

    我的最后一次尝试如果 Headers 更改,请尝试签入导航器:(

    if(errorResponse.status === 401 && error.error === 'token_expired') {
          const http = this.injector.get(HttpClient);
          let token = localStorage.getItem('token');
          return http.post(`${environment.apiBaseUrl}token/refresh`, {},
              {headers: new HttpHeaders().set('Authorization', `Bearer ${token}`)})
                     .switchMap(data => {
                            localStorage.setItem('currentUser', JSON.stringify(data));
                            localStorage.setItem('token', data.token);
                            const cloneRequest = request.clone(
                              {headers: new HttpHeaders()
                                  .set('Authorization', `Bearer ${data.token}`)
                              });
                            return next.handle(cloneRequest);
                        });
                }
    
  • 0

    解决并完美适用于多个解析器:

    export class RefreshTokenInterceptor implements HttpInterceptor {
    
    isRefreshingToken: boolean = false;
        tokenSubject: BehaviorSubject<string> = new BehaviorSubject<string>(null);
    
        constructor(private router: Router, private injector: Injector, private _loadingBar: SlimLoadingBarService) {
    
        }
    
        addToken(req: HttpRequest<any>, token: string): HttpRequest<any> {
            return req.clone({ setHeaders: { Authorization: `Bearer ${token}`}})
        }
    
        intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpSentEvent | HttpHeaderResponse | HttpProgressEvent | HttpResponse<any> | HttpUserEvent<any>> {
    
            return next.handle(this.addToken(req, localStorage.getItem('token')))
    
                .catch(error => {
    
                    if (error instanceof HttpErrorResponse) {
    
                        switch ((<HttpErrorResponse>error).status) {
                            case 400:
                                return this.handle400Error(error);
                            case 401:
                                return this.handle401Error(req, next);
                        }
    
                    } else {
    
                        return Observable.throw(error);
                    }
                });
        }
    
        handle401Error(req: HttpRequest<any>, next: HttpHandler) {
    
            if (!this.isRefreshingToken) {
                this.isRefreshingToken = true;
    
                // Reset here so that the following requests wait until the token
                // comes back from the refreshToken call.
                this.tokenSubject.next(null);
                let token = localStorage.getItem('token');
                const http = this.injector.get(HttpClient);
    
                return http.post<any>(`${environment.apiBaseUrl}token/refresh`, {},
                    {headers: new HttpHeaders().set('Authorization', `Bearer ${token}`)})
                    .switchMap((data: string) => {
    
                    if (data["token"]) {
                            this.tokenSubject.next(data["token"]);
                            return next.handle(this.addToken(req, data["token"]));
                        }
    
                        // If we don't get a new token, we are in trouble so logout.
                        return this.logoutUser();
                    })
                    .catch(error => {
                        // If there is an exception calling 'refreshToken', bad news so logout.
                        return this.logoutUser();
                    })
                    .finally(() => {
                        this.isRefreshingToken = false;
                    });
    
            } else {
    
                return this.tokenSubject
                    .filter(token => token != null)
                    .take(1)
                    .switchMap(token => {
                        return next.handle(this.addToken(req, token));
                    });
            }
        }
    
        logoutUser() {
            // Route to the login page (implementation up to you)
            localStorage.removeItem('currentUser');
            localStorage.removeItem('token');
    
            this.router.navigate(['./auth/login']);
    
            return Observable.throw("Error Logout");
        }
    
        handle400Error(error) {
            if (error && error.status === 400 && error.error && error.error.error === 'invalid_grant') {
                // If we get a 400 and the error message is 'invalid_grant', the token is no longer valid so logout.
                return this.logoutUser();
            }
    
            return Observable.throw(error);
        }
    

相关问题