В своем приложении я использую Angular HttpInterceptor для отслеживания каждого исходящего HTTP-запроса и добавления токена JWT, хранящегося в локальном хранилище, в заголовки запроса, чтобы бэкэнд-API мог авторизовать запрос путем проверки токена JWT.
Для этого перехватчик просто принимает HTTP-запрос и добавляет токен, устанавливая заголовки, а затем вызывает next.handle(req)
для распространения запроса.
Это делается с помощью приведенного ниже кода:
intercept(
req: HttpRequest<any>,
next: HttpHandler
): Observable<HttpEvent<any>> {
req = this.addHeaders(req);
return next.handle(req).pipe(
catchError(error => {
if (error instanceof HttpErrorResponse && error.status === 401) {
this._authService.signOut("unauthorized");
}
if (error instanceof HttpErrorResponse && error.status === 403) {
this._authService.signOut("expired");
}
return throwError(error);
})
);
}
/**
* Add access_token to header.
* @param req
*/
addHeaders(req: HttpRequest<any>): HttpRequest<any> {
return req.clone({
setHeaders: {
"Content-type": "application/json",
token: this._authService.getAuthenticationToken(),
jwt_token: this._authService.getAuthorizationToken(),
[this._config.subscriptionKeyName]: this._config.subscriptionKey || ""
}
});
}
Я хочу расширить эту функцию, чтобы перехватчик сначала проверил, чтобы убедиться, что токен JWT, сохраненный в локальном хранилище браузера, действителен. Если токен недействителен, сам перехватчик должен запустить отдельный HTTP-запрос для получения нового токена JWT из конечной точки API, который следует использовать для перезаписи старого, прежде чем продолжить исходный запрос.
Я пытаюсь понять, как построить такое поведение в моем коде Angular, но я продолжаю сталкиваться с бесконечными циклами.
Это код, который у меня есть в данный момент.
intercept(
req: HttpRequest<any>,
next: HttpHandler
): Observable<HttpEvent<any>> {
if (req.headers.has("newtokenrequest")) {
next.handle(req);
}
let jwtToken: string = this._authService.getLocalAuthorizationToken();
if (!this.checkJwtTokenIsValid(jwtToken)) { //Invalid token, get new one
const authTokenStream = this._authService.getApiAuthorizationToken$();
authTokenStream.subscribe(token => {
this._authService.setAuthorizationToken(token);
});
}
req = this.addHeaders(req);
return next.handle(req).pipe(
catchError(error => {
if (error instanceof HttpErrorResponse && error.status === 401) {
this._authService.signOut("unauthorized");
}
if (error instanceof HttpErrorResponse && error.status === 403) {
this._authService.signOut("expired");
}
return throwError(error);
})
);
}
И этот код возвращает Observable<string>
, который получает новый токен JWT.
getApiAuthorizationToken$(): Observable<string> {
let headers = new HttpHeaders();
headers = headers.set('newtokenrequest', 'true');
return this._httpClient.get<string>(this._configService.getConfig().teacherAuthAPI, {headers: headers});
}
Код возвращается к подписке authTokenStream, а затем возвращается к началу. Я предполагаю, что это потому, что подписка отправляет новый HTTP-запрос к бэкэнду для получения токена, однако этот запрос должен включать newtokenrequest
в качестве заголовка, который должен быть обнаружен методом перехвата, который должен вызвать next.handle(req)
,разрешение запроса продолжаться без дальнейшего взаимодействия, однако этого не происходит, и вместо этого код циклично зацикливается.
Редактировать: Обновить
intercept(
req: HttpRequest<any>,
next: HttpHandler
): Observable<HttpEvent<any>> {
if (req.headers.has("newtokenrequest")) {
return next.handle(req);
}
let jwtToken: string = this._authService.getLocalAuthorizationToken();
if (!this.checkJwtTokenIsValid(jwtToken) || jwtToken === undefined) { //Invalid token, get new one
const authTokenStream = this._authService.getApiAuthorizationToken$();
authTokenStream.subscribe(token => {
this._authService.setAuthorizationToken(token);
return next.handle(req);
});
}
req = this.addHeaders(req);
return next.handle(req).pipe(
catchError(error => {
if (error instanceof HttpErrorResponse && error.status === 401) {
this._authService.signOut("unauthorized");
}
if (error instanceof HttpErrorResponse && error.status === 403) {
this._authService.signOut("expired");
}
return throwError(error);
})
);
}
getApiAuthorizationToken$(): Observable<string> {
let headers = new HttpHeaders({
newtokenrequest: 'true'
});
// return this._httpClient.get<string>(this._configService.getConfig().teacherAuthAPI, {headers: headers});
const req = new HttpRequest<string>('GET', this._configService.getConfig().teacherAuthAPI, {headers});
let res = this._httpBackend.handle(req).pipe(map((response: HttpResponse<string>) => response.body),);
return res;
}