我正在尝试在收到 Angular 为 7 的错误 401 时自动执行刷新 token 请求。
我没有找到太多关于如何使用 angular 7 的文档,而且我之前没有 angular 或 rxjs 的知识,我变得有点疯狂
我认为它几乎完成了,但由于某种原因,第二个 next.handle(newReq) 没有发送请求(在 google chrome 网络调试器中只出现第一个请求)
我正在获取刷新响应并正确生成 processLoginResponse(res)
你可以在这里看到我的拦截器
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
let newReq = req.clone();
return next.handle(req).pipe(
catchError(error => {
if (error.status == 401) {
this._authenticationService.refresh().subscribe(
res => {
this._authenticationService.processLoginResponse(res);
newReq.headers.set("Authorization", "Bearer " + this._authenticationService.authResponse.token)
return next.handle(newReq)
},
error => {
this._authenticationService.logOut();
});
}
throw error;
})
);
最佳答案
您必须区分所有请求。例如,您不想拦截您的登录请求,也不想拦截刷新 token 请求。 SwitchMap 是您最好的 friend ,因为您需要取消一些调用以等待 token 刷新。
所以您要做的是首先检查状态为 401(未授权)的错误响应:
return next.handle(this.addToken(req, this.userService.getAccessToken()))
.pipe(catchError(err => {
if (err instanceof HttpErrorResponse) {
// token is expired refresh and try again
if (err.status === 401) {
return this.handleUnauthorized(req, next);
}
// default error handler
return this.handleError(err);
} else {
return observableThrowError(err);
}
}));
在您的 handleUnauthorized 函数中,您必须刷新 token 并同时跳过所有进一步的请求:
handleUnauthorized (req: HttpRequest<any>, next: HttpHandler): Observable<any> {
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);
// get a new token via userService.refreshToken
return this.userService.refreshToken()
.pipe(switchMap((newToken: string) => {
// did we get a new token retry previous request
if (newToken) {
this.tokenSubject.next(newToken);
return next.handle(this.addToken(req, newToken));
}
// If we don't get a new token, we are in trouble so logout.
this.userService.doLogout();
return observableThrowError('');
})
, catchError(error => {
// If there is an exception calling 'refreshToken', bad news so logout.
this.userService.doLogout();
return observableThrowError('');
})
, finalize(() => {
this.isRefreshingToken = false;
})
);
} else {
return this.tokenSubject
.pipe(
filter(token => token != null)
, take(1)
, switchMap(token => {
return next.handle(this.addToken(req, token));
})
);
}
}
我们在拦截器类上有一个属性,用于检查是否已经有刷新 token 请求在运行:this.isRefreshingToken = true;
因为当您触发多个未经授权的请求时,您不希望有多个刷新请求。
if (!this.isRefreshingToken)
中的所有内容部分是关于刷新您的 token 并再次尝试之前的请求。
在 else
中处理的一切适用于所有请求,与此同时,当您的 userService 刷新 token 时,将返回一个 tokenSubject,当 token 准备好时 this.tokenSubject.next(newToken);
每个跳过的请求都将重试。
这篇文章是拦截器的起源灵感:https://www.intertech.com/angular-4-tutorial-handling-refresh-token-with-new-httpinterceptor/
编辑:
TokenSubject 实际上是一个行为主题:tokenSubject: BehaviorSubject<string> = new BehaviorSubject<string>(null);
,这意味着任何新订阅者都将获得流中的当前值,这将是我们上次调用 this.tokenSubject.next(newToken)
时的旧 token .
与 next(null)
每个新订阅者都不会触发 switchMap
部分,这就是为什么filter(token => token != null)
是必要的。
在 this.tokenSubject.next(newToken)
之后使用新 token 再次调用每个订阅者都会触发 switchMap
与新 token 分开。希望现在更清楚
编辑 21.09.2020
修复链接
关于angular - 尝试使用 Angular 7 中的拦截器在刷新 token 后重复 http 请求,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54327548/