我正在尝试使用 JWT 访问和刷新令牌为存档网站实施身份验证。在刷新令牌调用期间,它会进行一定程度的身份验证,但效率不高,如下图所示,当存在多个 API 调用时,刷新令牌也会被多次调用,最后一个将被验证并刷新会话。我想避免,在获得身份验证后只进行一次刷新调用,然后希望它继续进行 API 调用。
下面是我现在尝试的代码,关于结构,我有一个身份验证拦截器,它拦截请求并在后端添加用于身份验证的标头。另一个错误拦截器会拦截 Http 错误,如果发生 401 错误,它会拦截它并调用刷新函数来处理刷新令牌策略。
身份验证拦截器:
@Injectable()
export class AuthInterceptor implements HttpInterceptor {
constructor(private storage: StorageService, private authService: AuthService) { }
intercept(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {
const storedTokens = this.authService.getTokens()
if (storedTokens.access_token && !request.url.includes('/auth/refresh') ) {
const cloned = request.clone({
headers: request.headers.set("Authorization", storedTokens.access_token)
});
return next.handle(cloned);
}
else if (storedTokens.refresh_token && request.url.includes('/auth/refresh')){
const cloned = request.clone({
headers: request.headers.set("Authorization", storedTokens.refresh_token)
});
return next.handle(cloned);
}
else {
return next.handle(request);
}
}
}
错误拦截器:
@Injectable()
export class ErrorInterceptor implements HttpInterceptor {
constructor(private router: Router, private authService: AuthService) { }
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
return next.handle(request).pipe(
catchError((error: HttpErrorResponse) => {
if (error.status === 401) {
// Access token has expired, attempt to refresh token
return this.authService.refresh().pipe(
switchMap(() => {
const storedTokens = this.authService.getTokens()
const updatedRequest = request.clone({
headers: request.headers.set("Authorization", `${storedTokens.access_token}`)
});
return next.handle(updatedRequest);
}),
catchError((refreshError: any) => {
// Token refresh failed or refresh token is invalid
// Redirect user to login page
this.authService.logout();
return throwError(refreshError);
})
);
} else if (error.status === 403) {
// Unauthorized, redirect to login page
this.authService.logout();
}
return throwError(() => new Error(error.message));
})
);
}
}
我认为这是因为令牌已过期并且组件仍然访问了多个 API(不是 auth API)。 因此,在收到 401 错误后,拦截器将命中 API 以获取刷新令牌,同时组件会命中 ngOnInit 上的多个 API。
对于解决方案,我认为你可以尝试两种选择:
首先,创建一个全局状态来验证或检查令牌,这意味着未过期且有效。然后包装hit API的函数,只有在token有效的情况下才执行该函数。
getProductList(): void {
if (isValidToken) {
this.serviceName.hitApi().subscribe...;
}
}
其次,将hit API的功能分配给订阅类型的变量,并取消订阅取消网络上重复的HTTP调用,例如:
getProductList(): void {
this.$subscription?.unsubscribe()
this.$subscription = this.serviceName.hitApi().subscribe...;
}
希望可以帮到你。谢谢你。