Angular 拦截器 - 如果刷新令牌拦截器失败,我该如何注销?
Angular interceptor - how do I logout if refresh token interceptor fails?
信息
我正在创建一个拦截器,以便在收到 401 时使用我的刷新令牌来更新我的访问令牌。现在的工作流程如下所示:
发送请求 > 获取 401 > 发送刷新请求 > 更新访问令牌 > 发送新请求
我目前正在使用 promises 而不是 observables。
问题
如果上次请求失败,我该如何注销?
发送请求 > 收到 401 > 发送刷新请求 > 更新访问令牌 > 发送新请求 > 失败 > 注销
我有一个简单的注销方法,但我找不到将它放在拦截器中的什么位置。
代码
export class RefreshInterceptor implements HttpInterceptor {
currentUser: User | null = null;
private isRefreshing = false;
private refreshTokenSubject: BehaviorSubject<any> = new BehaviorSubject<any>(
null
);
constructor(private authenticationService: AuthenticationService) {
this.authenticationService.currentUser.subscribe(
user => (this.currentUser = user)
);
}
intercept(
request: HttpRequest<any>,
next: HttpHandler
): Observable<HttpEvent<any>> {
return next.handle(request).pipe(
catchError(error => {
// check if user is signed in
if (!this.currentUser) {
return throwError(error);
}
// handle only 401 error
if (error instanceof HttpErrorResponse && error.status === 401) {
return from(this.handle401Error(request, next));
} else {
return throwError(error);
}
})
);
}
/**
* Adds the new access token as a bearer header to the request
* @param request - the request
* @param token - the new access token
*/
private async addToken(request: HttpRequest<any>, token: string) {
const currentUser = this.authenticationService.currentUserValue;
if (currentUser && currentUser.accessToken) {
return request.clone({
setHeaders: {
Authorization: `Bearer ${token}`
}
});
}
return request;
}
private async handle401Error(request: HttpRequest<any>, next: HttpHandler) {
// check if it is currently refreshing or not
if (!this.isRefreshing) {
this.isRefreshing = true;
this.refreshTokenSubject.next(null);
// send refresh request
const token = await this.authenticationService.getRefresh();
// update bearer token
const newRequest = await this.addToken(request, token);
// update values for next request
this.isRefreshing = false;
this.refreshTokenSubject.next(token);
return next.handle(newRequest).toPromise();
} else {
const token = this.refreshTokenSubject.value();
const newRequest = await this.addToken(request, token);
return next.handle(newRequest).toPromise();
}
}
}
我用下面的方法解决了:
- 修改了传出的更改请求的 header(添加了重试 header 以便我稍后可以识别它)。
- 为注销创建了一个新的拦截器
- 查找重试请求 header。签署了该请求。
刷新令牌拦截器
if (currentUser && currentUser.accessToken) {
return request.clone({
setHeaders: {
Authorization: `Bearer ${token}`,
Retry: "true"
}
});
}
注销拦截器
@Injectable()
export class LogoutInterceptor implements HttpInterceptor {
constructor(private authenticationService: AuthenticationService) {}
intercept(
request: HttpRequest<any>,
next: HttpHandler
): Observable<HttpEvent<any>> {
return next.handle(request).pipe(
catchError(error => {
// handle only 401 error
if (error instanceof HttpErrorResponse && error.status === 401) {
from(this.handleRequest(request));
return throwError(error);
}
return next.handle(request);
})
);
}
private async handleRequest(request: HttpRequest<any>) {
const isRetriedRequest = request.headers.get("retry");
if (isRetriedRequest) {
await this.authenticationService.logout();
}
}
}
信息
我正在创建一个拦截器,以便在收到 401 时使用我的刷新令牌来更新我的访问令牌。现在的工作流程如下所示:
发送请求 > 获取 401 > 发送刷新请求 > 更新访问令牌 > 发送新请求
我目前正在使用 promises 而不是 observables。
问题
如果上次请求失败,我该如何注销?
发送请求 > 收到 401 > 发送刷新请求 > 更新访问令牌 > 发送新请求 > 失败 > 注销
我有一个简单的注销方法,但我找不到将它放在拦截器中的什么位置。
代码
export class RefreshInterceptor implements HttpInterceptor {
currentUser: User | null = null;
private isRefreshing = false;
private refreshTokenSubject: BehaviorSubject<any> = new BehaviorSubject<any>(
null
);
constructor(private authenticationService: AuthenticationService) {
this.authenticationService.currentUser.subscribe(
user => (this.currentUser = user)
);
}
intercept(
request: HttpRequest<any>,
next: HttpHandler
): Observable<HttpEvent<any>> {
return next.handle(request).pipe(
catchError(error => {
// check if user is signed in
if (!this.currentUser) {
return throwError(error);
}
// handle only 401 error
if (error instanceof HttpErrorResponse && error.status === 401) {
return from(this.handle401Error(request, next));
} else {
return throwError(error);
}
})
);
}
/**
* Adds the new access token as a bearer header to the request
* @param request - the request
* @param token - the new access token
*/
private async addToken(request: HttpRequest<any>, token: string) {
const currentUser = this.authenticationService.currentUserValue;
if (currentUser && currentUser.accessToken) {
return request.clone({
setHeaders: {
Authorization: `Bearer ${token}`
}
});
}
return request;
}
private async handle401Error(request: HttpRequest<any>, next: HttpHandler) {
// check if it is currently refreshing or not
if (!this.isRefreshing) {
this.isRefreshing = true;
this.refreshTokenSubject.next(null);
// send refresh request
const token = await this.authenticationService.getRefresh();
// update bearer token
const newRequest = await this.addToken(request, token);
// update values for next request
this.isRefreshing = false;
this.refreshTokenSubject.next(token);
return next.handle(newRequest).toPromise();
} else {
const token = this.refreshTokenSubject.value();
const newRequest = await this.addToken(request, token);
return next.handle(newRequest).toPromise();
}
}
}
我用下面的方法解决了:
- 修改了传出的更改请求的 header(添加了重试 header 以便我稍后可以识别它)。
- 为注销创建了一个新的拦截器
- 查找重试请求 header。签署了该请求。
刷新令牌拦截器
if (currentUser && currentUser.accessToken) {
return request.clone({
setHeaders: {
Authorization: `Bearer ${token}`,
Retry: "true"
}
});
}
注销拦截器
@Injectable()
export class LogoutInterceptor implements HttpInterceptor {
constructor(private authenticationService: AuthenticationService) {}
intercept(
request: HttpRequest<any>,
next: HttpHandler
): Observable<HttpEvent<any>> {
return next.handle(request).pipe(
catchError(error => {
// handle only 401 error
if (error instanceof HttpErrorResponse && error.status === 401) {
from(this.handleRequest(request));
return throwError(error);
}
return next.handle(request);
})
);
}
private async handleRequest(request: HttpRequest<any>) {
const isRetriedRequest = request.headers.get("retry");
if (isRetriedRequest) {
await this.authenticationService.logout();
}
}
}