我目前正在努力在我的 Angular 前端实现刷新令牌机制。当我的后端返回 401 错误时,我的错误拦截器会处理它。但是,我面临一个问题:当我的访问令牌(JWT 令牌)过期并且我重新加载页面时,会向后端发送三个请求以检索数据。所有这些都会导致 401 错误,因为它们需要授权。
我的拦截器的作用是使用本地存储中的过期访问令牌并将其发送到刷新令牌端点以获取新的访问令牌和刷新令牌。当重新加载页面时同时触发多个请求时,就会出现问题。第一个请求被拦截器捕获,该拦截器将新的访问令牌存储在本地存储中。但是,后续的两个请求无法获取新的访问令牌,因为它们仍然使用存储中的旧令牌。所有三个 401 响应同时触发错误拦截器。
有解决方案来解决这种行为吗?我正在寻求有关如何处理并发请求并确保它们全部使用刷新的访问令牌的指导。
谢谢您的帮助!
错误拦截器
@Injectable({ providedIn: 'root' })
export class ErrorInterceptor implements HttpInterceptor {
constructor(private readonly authService: AuthService) {}
intercept(
req: HttpRequest<any>,
next: HttpHandler
): Observable<HttpEvent<any>> {
return next.handle(req).pipe(
catchError((error: HttpErrorResponse) => {
if (error.status === 401 && !req.url.includes('auth/login')) {
return this.handle401HttpError(error, req, next);
}
return next.handle(req);
})
);
}
private handle401HttpError(
error: HttpErrorResponse,
req: HttpRequest<any>,
next: HttpHandler
): Observable<any> {
var currentAccessToken: string = localStorage.getItem('token') as string;
var currentRefreshToken: string = localStorage.getItem(
'refreshToken'
) as string;
var request: IRefreshTokenRequest = {
accessToken: currentAccessToken,
refreshToken: currentRefreshToken,
};
this.authService
.refreshToken(request)
.pipe(
switchMap((user: IUser | null) => {
if (user) {
localStorage.setItem('token', user.token);
localStorage.setItem('refreshToken', user.refreshToken);
req = req.clone({
setHeaders: {
Authorization: `Bearer ${user?.token}`,
},
});
return next.handle(req);
}
return throwError(() => error);
}),
catchError((err: HttpErrorResponse) => {
this.authService.logout();
return throwError(() => err);
})
)
.subscribe();
return throwError(() => error);
}
}
尝试使用
refreshToken()
中的 HttpBackend
处理程序定义除 http 拦截器之外的 AuthService
服务调用。这是一个实现示例:
export class AuthService {
private httpClient: HttpClient;
constructor(handler: HttpBackend) {
this.httpClient = new HttpClient(handler);
}
refreshToken(request: any): Observable<any> {
const header = {
headers: new HttpHeaders()
.set('Authorization', localStorage.getItem('token'))
}
return this.httpClient.post<any>(BASE_URL + '/refreshToken', request, header);
}
//..
}
这应该可以解决您的问题。