我正在尝试在Angular前端中实现令牌拦截器,但是当前的实现存在一个问题。
我的拦截器看起来像这样:
intercept(request: HttpRequest<any>,next: HttpHandler): Observable<HttpEvent<any>> {
if (request.headers.get("No-Auth") === "True") {
return next.handle(request);
}
return next.handle(request).pipe(
catchError(error => {
if (error instanceof HttpErrorResponse && error.status === 401) {
this.handle404Error(request, next);
} else {
return throwError(error);
}
})
);
}
和handle404Error函数:
handle404Error(request: HttpRequest<any>, next: HttpHandler) {
if (!this.isRefreshing) {
this.isRefreshing = true;
this.refreshTokenSubject.next(null);
return this.authService.refreshToken().subscribe(
(res: any) => {
if (Boolean(res) === true) {
this.isRefreshing = false;
this.refreshTokenSubject.next(res);
return next.handle(request);
} else {
return this.authService.logout();
}
},
err => {
return this.authService.logout();
},
() => {
this.isRefreshing = false;
}
);
} else {
return this.refreshTokenSubject.pipe(
filter(res => res != null),
take(1),
switchMap(() => {
return next.handle(request);
})
);
}
}
当我进行API调用和服务器返回404时,我的拦截器将调用/token
端点以获取新的JWT。
请注意,我将JWT存储在服务器上(内存中)的会话对象中,并且在客户端上,我只有sessionID和刷新令牌作为HttpOnly cookie。
问题是,当我进行API调用且JWT无效时,它将设置新的JWT,但不会再次发送先前的请求。 (在JWT过期后,我必须按两次按钮才能获取数据。)
您有什么想法,如何实现此逻辑?
感谢您的帮助。
答案 0 :(得分:1)
第一件事是不对的-您在拦截器内subscribe
,因此您会嵌套订阅,因为Angular在内部订阅了您的请求。
您真正想要的是从return next.handle(request);
返回您的catchError
。示例:
return next.handle(request).pipe(
catchError(error => {
if (error instanceof HttpErrorResponse && error.status === 401) {
return this.handle404Error(request, next);
} else {
return throwError(error);
}
})
);
handle404Error(request: HttpRequest<any>, next: HttpHandler) {
if (!this.isRefreshing) {
this.isRefreshing = true;
this.refreshTokenSubject.next(null);
return this.authService.refreshToken().pipe(
mergeMap((res: any) => {
if (Boolean(res) === true) {
this.isRefreshing = false;
this.refreshTokenSubject.next(res);
return next.handle(request);
} else {
/* return */ this.authService.logout();
return of(undefined) // added this because I dont know what this.authService.logout() returns. It must be an Observable
}
}),
catchError((err) => {
/* return */ this.authService.logout();
return throwError(err);
}))
} else {
return this.refreshTokenSubject.pipe(
filter(res => res != null),
take(1),
switchMap(() => {
return next.handle(request);
})
);
}
}
请注意,我没有检查任何错别字。我也丢掉了这个方块:
() => {
this.isRefreshing = false;
}
如果要在this.authService.refreshToken()
完成时执行它,可以在finalize
和mergeMap
旁边添加catchError
运算符。
解释这里实际发生的事情很简单-您可以从intercept
方法返回一个可观察到的结果。只需将所有API调用通过管道传递,返回的流将执行它们。