无法在拦截器中进行订阅
Can not make subscription in interceptor
我正在制作 JWT 拦截器来处理 401 错误。我的想法是将 401 之后的所有原始请求收集到数组中,然后刷新我的令牌,然后将新的 header 添加到我的请求中。这是代码:
type CallerRequest = {
subscriber: Subscriber<any>;
failedRequest: HttpRequest<any>;
};
@Injectable()
export class RefreshTokenInterceptor implements HttpInterceptor {
private refreshInProgress: boolean;
private requests: CallerRequest[] = [];
constructor(
public authService: AuthService,
public customersService: CustomersService,
private http: HttpClient
) {
this.refreshInProgress = false;
}
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
if (!req.url.includes('api/')) {
return next.handle(req);
}
const observable = new Observable<HttpEvent<any>>((subscriber) => {
const originalRequestSubscription = next.handle(req)
.subscribe((response) => {
subscriber.next(response);
},
(err) => {
if (err.status === 401) {
this.handleUnauthorizedError(subscriber, req);
} else {
subscriber.error(err);
}
},
() => {
subscriber.complete();
});
return () => {
originalRequestSubscription.unsubscribe();
};
});
return observable;
}
private addToken(request: HttpRequest<any>, token: string) {
return request.clone({setHeaders: {'Authorization': `Bearer ${token}`}});
}
private handleUnauthorizedError(subscriber: Subscriber<any>, request: HttpRequest<any>) {
this.requests.push({ subscriber, failedRequest: request });
if (!this.refreshInProgress) {
this.refreshInProgress = true;
console.log('go refresh');
this.authService.refreshToken()
.pipe(
finalize(() => {
this.refreshInProgress = false;
})
)
.subscribe(
result => {
console.log(result); // <-- I cannot get here
this.repeatFailedRequests(this.authService.getAccessToken());
},
err => {
console.log(err); // <-- And cannot get here
}
)
}
}
private repeatFailedRequests(authHeader: string) {
this.requests.forEach((c) => {
const requestWithNewToken = this.addToken(c.failedRequest, authHeader);
this.repeatRequest(requestWithNewToken, c.subscriber);
});
this.requests = [];
}
private repeatRequest(requestWithNewToken: HttpRequest<any>, subscriber: Subscriber<any>) {
this.http.request(requestWithNewToken).subscribe((res) => {
subscriber.next(res);
},
(err) => {
if (err.status === 401) {
this.authService.removeTokens();
}
subscriber.error(err);
},
() => {
subscriber.complete();
});
}
}
看看我的 handleUnauthorizedError 方法。我无法在那里获得结果。
顺便说一句,这是我的 refreshToken:
refreshToken() {
return this.post('api/v0/jwt/refresh/', {refresh: this.getRefreshToken()})
.pipe(tap((tokens: Tokens) => {
this.storeTokens(tokens);
}));
}
我做错了什么,我该如何正确解决这个问题?谢谢!
添加:
如果我从这个问题 Angular 4 Interceptor retry requests after token refresh(最流行的答案)中尝试 JWT 拦截器,我会遇到同样的问题 - 我的 RefreshToken() 结果未得到处理。
我认为问题出在你的拦截方法上,因为你包装了原始的 Observable,它没有被正确调用。
尝试这样的事情:
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
if (!req.url.includes('api/')) {
return next.handle(req);
}
return next
.handle(req)
.catch((err) => {
if (err.status === 401) {
this.handleUnauthorizedError(req);
} else {
return next.handle(req);
}
})
);
}
问题在于在 refreshInProgress 时正确处理 JWT 刷新请求。这是完美运行的代码:
type CallerRequest = {
subscriber: Subscriber<any>;
failedRequest: HttpRequest<any>;
};
@Injectable()
export class AuthTokenInterceptor implements HttpInterceptor {
private refreshInProgress: boolean;
private requests: CallerRequest[] = [];
private refreshJWTReuest: Subscription;
private straightRequests: string[];
constructor(
public authService: AuthService,
public customersService: CustomersService,
private http: HttpClient
) {
this.refreshInProgress = false;
this.straightRequests = ['currency/'];
}
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
if (this.straightRequests.some(v => req.url.includes(v))) {
return next.handle(req);
}
if (req.url.includes('api/v0/jwt/create/')) {
this.refreshInProgress = false;
}
const observable = new Observable<HttpEvent<any>>((subscriber) => {
const originalRequestSubscription = next.handle(this.addToken(req, this.authService.getAccessToken()))
.subscribe((response) => {
subscriber.next(response);
},
(err) => {
if (err.status === 401) {
if (req.url.includes('api/v0/jwt/refresh/')) {
this.refreshJWTReuest.unsubscribe();
this.requests = [];
this.authService.logout();
}
this.handleUnauthorizedError(subscriber, req);
} else {
// notification!!!
subscriber.error(err);
}
},
() => {
subscriber.complete();
});
return () => {
originalRequestSubscription.unsubscribe();
};
});
return observable;
}
private addToken(request: HttpRequest<any>, token: string) {
return request.clone({setHeaders: {'Authorization': `Bearer ${token}`}});
}
private handleUnauthorizedError(subscriber: Subscriber<any>, request: HttpRequest<any>) {
if (!this.refreshInProgress) {
this.refreshInProgress = true;
this.refreshJWTReuest = this.authService.refreshToken()
.subscribe(
result => {
this.refreshInProgress = false;
this.repeatFailedRequests(result.access);
},
err => {
this.authService.logout();
console.log(err);
}
);
}
if (!request.url.includes('api/v0/jwt/refresh/')) {
// avoid refresh requests while refreshInProgress
// this solves a small bug after logout()
this.requests.push({ subscriber, failedRequest: request });
}
}
private repeatFailedRequests(authHeader: string) {
this.requests.forEach((c) => {
const requestWithNewToken = this.addToken(c.failedRequest, authHeader);
this.repeatRequest(requestWithNewToken, c.subscriber);
});
this.refreshJWTReuest.unsubscribe();
this.requests = [];
}
private repeatRequest(requestWithNewToken: HttpRequest<any>, subscriber: Subscriber<any>) {
this.http.request(requestWithNewToken).subscribe((res) => {
subscriber.next(res);
},
(err) => {
if (err.status === 401) {
this.authService.logout();
}
subscriber.error(err);
},
() => {
subscriber.complete();
});
}
}
我正在制作 JWT 拦截器来处理 401 错误。我的想法是将 401 之后的所有原始请求收集到数组中,然后刷新我的令牌,然后将新的 header 添加到我的请求中。这是代码:
type CallerRequest = {
subscriber: Subscriber<any>;
failedRequest: HttpRequest<any>;
};
@Injectable()
export class RefreshTokenInterceptor implements HttpInterceptor {
private refreshInProgress: boolean;
private requests: CallerRequest[] = [];
constructor(
public authService: AuthService,
public customersService: CustomersService,
private http: HttpClient
) {
this.refreshInProgress = false;
}
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
if (!req.url.includes('api/')) {
return next.handle(req);
}
const observable = new Observable<HttpEvent<any>>((subscriber) => {
const originalRequestSubscription = next.handle(req)
.subscribe((response) => {
subscriber.next(response);
},
(err) => {
if (err.status === 401) {
this.handleUnauthorizedError(subscriber, req);
} else {
subscriber.error(err);
}
},
() => {
subscriber.complete();
});
return () => {
originalRequestSubscription.unsubscribe();
};
});
return observable;
}
private addToken(request: HttpRequest<any>, token: string) {
return request.clone({setHeaders: {'Authorization': `Bearer ${token}`}});
}
private handleUnauthorizedError(subscriber: Subscriber<any>, request: HttpRequest<any>) {
this.requests.push({ subscriber, failedRequest: request });
if (!this.refreshInProgress) {
this.refreshInProgress = true;
console.log('go refresh');
this.authService.refreshToken()
.pipe(
finalize(() => {
this.refreshInProgress = false;
})
)
.subscribe(
result => {
console.log(result); // <-- I cannot get here
this.repeatFailedRequests(this.authService.getAccessToken());
},
err => {
console.log(err); // <-- And cannot get here
}
)
}
}
private repeatFailedRequests(authHeader: string) {
this.requests.forEach((c) => {
const requestWithNewToken = this.addToken(c.failedRequest, authHeader);
this.repeatRequest(requestWithNewToken, c.subscriber);
});
this.requests = [];
}
private repeatRequest(requestWithNewToken: HttpRequest<any>, subscriber: Subscriber<any>) {
this.http.request(requestWithNewToken).subscribe((res) => {
subscriber.next(res);
},
(err) => {
if (err.status === 401) {
this.authService.removeTokens();
}
subscriber.error(err);
},
() => {
subscriber.complete();
});
}
}
看看我的 handleUnauthorizedError 方法。我无法在那里获得结果。
顺便说一句,这是我的 refreshToken:
refreshToken() {
return this.post('api/v0/jwt/refresh/', {refresh: this.getRefreshToken()})
.pipe(tap((tokens: Tokens) => {
this.storeTokens(tokens);
}));
}
我做错了什么,我该如何正确解决这个问题?谢谢!
添加:
如果我从这个问题 Angular 4 Interceptor retry requests after token refresh(最流行的答案)中尝试 JWT 拦截器,我会遇到同样的问题 - 我的 RefreshToken() 结果未得到处理。
我认为问题出在你的拦截方法上,因为你包装了原始的 Observable,它没有被正确调用。
尝试这样的事情:
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
if (!req.url.includes('api/')) {
return next.handle(req);
}
return next
.handle(req)
.catch((err) => {
if (err.status === 401) {
this.handleUnauthorizedError(req);
} else {
return next.handle(req);
}
})
);
}
问题在于在 refreshInProgress 时正确处理 JWT 刷新请求。这是完美运行的代码:
type CallerRequest = {
subscriber: Subscriber<any>;
failedRequest: HttpRequest<any>;
};
@Injectable()
export class AuthTokenInterceptor implements HttpInterceptor {
private refreshInProgress: boolean;
private requests: CallerRequest[] = [];
private refreshJWTReuest: Subscription;
private straightRequests: string[];
constructor(
public authService: AuthService,
public customersService: CustomersService,
private http: HttpClient
) {
this.refreshInProgress = false;
this.straightRequests = ['currency/'];
}
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
if (this.straightRequests.some(v => req.url.includes(v))) {
return next.handle(req);
}
if (req.url.includes('api/v0/jwt/create/')) {
this.refreshInProgress = false;
}
const observable = new Observable<HttpEvent<any>>((subscriber) => {
const originalRequestSubscription = next.handle(this.addToken(req, this.authService.getAccessToken()))
.subscribe((response) => {
subscriber.next(response);
},
(err) => {
if (err.status === 401) {
if (req.url.includes('api/v0/jwt/refresh/')) {
this.refreshJWTReuest.unsubscribe();
this.requests = [];
this.authService.logout();
}
this.handleUnauthorizedError(subscriber, req);
} else {
// notification!!!
subscriber.error(err);
}
},
() => {
subscriber.complete();
});
return () => {
originalRequestSubscription.unsubscribe();
};
});
return observable;
}
private addToken(request: HttpRequest<any>, token: string) {
return request.clone({setHeaders: {'Authorization': `Bearer ${token}`}});
}
private handleUnauthorizedError(subscriber: Subscriber<any>, request: HttpRequest<any>) {
if (!this.refreshInProgress) {
this.refreshInProgress = true;
this.refreshJWTReuest = this.authService.refreshToken()
.subscribe(
result => {
this.refreshInProgress = false;
this.repeatFailedRequests(result.access);
},
err => {
this.authService.logout();
console.log(err);
}
);
}
if (!request.url.includes('api/v0/jwt/refresh/')) {
// avoid refresh requests while refreshInProgress
// this solves a small bug after logout()
this.requests.push({ subscriber, failedRequest: request });
}
}
private repeatFailedRequests(authHeader: string) {
this.requests.forEach((c) => {
const requestWithNewToken = this.addToken(c.failedRequest, authHeader);
this.repeatRequest(requestWithNewToken, c.subscriber);
});
this.refreshJWTReuest.unsubscribe();
this.requests = [];
}
private repeatRequest(requestWithNewToken: HttpRequest<any>, subscriber: Subscriber<any>) {
this.http.request(requestWithNewToken).subscribe((res) => {
subscriber.next(res);
},
(err) => {
if (err.status === 401) {
this.authService.logout();
}
subscriber.error(err);
},
() => {
subscriber.complete();
});
}
}