Angular 4.3 - HTTP 拦截器 - 刷新 JWT 令牌
Angular 4.3 - HTTP Interceptor - refresh JWT token
我需要(在拦截器 class 中)对 403 禁止 HTTP 状态(对 obtain/refresh)JWT 令牌做出反应,并使用新令牌重试请求。
在下面的代码中,当服务器 return 错误响应时,它会转到成功回调(而不是像我预期的那样进入错误回调)并且事件是 typeof 对象(这在错误响应的反应中是无用的) .事件对象如下所示:
{type:0}.
问题:
-如何正确处理httpErrorResponse (403 Forbidden) in
HttpInterceptor 当我需要刷新 accessToken 并重试 http 请求时?
import {
HttpInterceptor,
HttpRequest,
HttpResponse,
HttpHandler,
HttpEvent
} from '@angular/common/http';
import 'rxjs/add/operator/map';
@Injectable()
class JWTInterceptor implements HttpInterceptor {
constructor(private tokenService: TokenService) {}
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
let myHeaders = req.headers;
if (this.tokenService.accessToken) {
myHeaders = myHeaders.append('Authorization',`${this.tokenService.accessToken.token_type} ${this.tokenService.accessToken.access_token}`)
}
const authReq = req.clone({headers: myHeaders});
return next.handle(authReq).map((event: HttpEvent<any>) => {
if (event instanceof HttpResponse) {
// success callback
}
}, (err: any) => {
if (err instanceof HttpErrorResponse {
if (err.status === 403) {
// error callback
this.tokenService.obtainAccessToken()
}
}
})
.retry(1);
}
}
您需要从 RxJS 添加 catch
运算符。这就是错误所在,您可以相应地处理它。
当您收到状态为 0 的错误时,这很可能意味着远程服务器已关闭并且无法建立连接。
看看我的示例逻辑:
this.http.request(url, options)
.map((res: Response) => res.json())
.catch((error: any) => {
const err = error.json();
// Refresh JWT
if (err.status === 403) {
// Add your token refresh logic here.
}
return Observable.throw(err);
});
为了让您的刷新逻辑通过拦截器,您需要 return 调用,函数也应该 return 和 Observable
。比如修改上面的原逻辑:
this.http.request(url, options)
.map((res: Response) => res.json())
.catch((error: any) => {
const err = error.json();
// Refresh JWT
if (err.status === 403) {
// refreshToken makes another HTTP call and returns an Observable.
return this.refreshToken(...);
}
return Observable.throw(err);
});
如果您希望能够重试原始请求,您可以做的是传递原始请求数据,以便在成功刷新令牌后您可以再次进行调用。例如,这就是您的 refreshToken
函数的样子:
refreshToken(url: stirng, options: RequestOptionsArgs, body: any, tokenData: any): Observable<any>
return this.post(`${this.url}/token/refresh`, tokenData)
.flatMap((res: any) => {
// This is where I retry the original request
return this.request(url, options, body);
});
}
我对这个问题的最终解决方案:
@Injectable()
export class WebApiInterceptor implements HttpInterceptor {
constructor(private tokenService: TokenService) {
}
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
console.log('*An intercepted httpRequest*', req, this.tokenService.accessToken);
const authReq = this.authenticateRequest(req);
console.log('*Updated httpRequest*', authReq);
return next.handle(authReq)
.map((event: HttpEvent<any>) => {
if (event instanceof HttpResponse) {
console.log('*An intercepted httpResponse*', event);
return event;
}
})
.catch((error: any) => {
if (error instanceof HttpErrorResponse) {
if (error.status === 403 && error.url !== environment.authEndpoint) {
return this.tokenService
.obtainAccessToken()
.flatMap((token) => {
const authReqRepeat = this.authenticateRequest(req);
console.log('*Repeating httpRequest*', authReqRepeat);
return next.handle(authReqRepeat);
});
}
} else {
return Observable.throw(error);
}
})
}
}
函数
authenticateRequest(req)
只是将授权 header 添加到原始请求的副本中
函数
obtainAccessToken()
从授权服务器获取新的令牌并存储它
只想分享对我有用的东西:
@Injectable()
export class AutoReLoginInterceptor implements HttpInterceptor {
constructor() {
}
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
// as we want to intercept the possible errors, instead of directly returning the request execution, we return an Observable to control EVERYTHING
return new Observable<HttpEvent<any>>(subscriber => {
// first try for the request
next.handle(req)
.subscribe((event: HttpEvent<any>) => {
if (event instanceof HttpResponse) {
// the request went well and we have valid response
// give response to user and complete the subscription
subscriber.next(event);
subscriber.complete();
}
},
error => {
if (error instanceof HttpErrorResponse && error.status === 401) {
console.log('401 error, trying to re-login');
// try to re-log the user
this.reLogin().subscribe(authToken => {
// re-login successful -> create new headers with the new auth token
let newRequest = req.clone({
headers: req.headers.set('Authorization', authToken)
});
// retry the request with the new token
next.handle(newRequest)
.subscribe(newEvent => {
if (newEvent instanceof HttpResponse) {
// the second try went well and we have valid response
// give response to user and complete the subscription
subscriber.next(newEvent);
subscriber.complete();
}
}, error => {
// second try went wrong -> throw error to subscriber
subscriber.error(error);
});
});
} else {
// the error was not related to auth token -> throw error to subscriber
subscriber.error(error);
}
});
});
}
/**
* Try to re-login the user.
*/
private reLogin(): Observable<string> {
// obtain new authorization token and return it
}
}
import { Injectable } from '@angular/core';
import { HttpRequest, HttpHandler, HttpEvent, HttpInterceptor } from '@angular/common/http';
import { Observable } from 'rxjs';
@Injectable()
export class JwtInterceptor implements HttpInterceptor {
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
// add authorization header with jwt token if available
const currentUser = JSON.parse(sessionStorage.getItem('currentUser'));
console.log('Interceter called');
console.log(currentUser);
// const re = 'https://newsapi.org';
const re = '/user';
if (request.url.search(re) === -1) {
if (currentUser && currentUser.token) {
console.log('Token is being added....!!!!!');
// console.log(currentUser.token);
request = request.clone({
setHeaders: {
Authorisation: `Token ${currentUser.token}`,
}
});
}
console.log('Request Sent :');
console.log(request);
}
return next.handle(request);
}
}
我需要(在拦截器 class 中)对 403 禁止 HTTP 状态(对 obtain/refresh)JWT 令牌做出反应,并使用新令牌重试请求。
在下面的代码中,当服务器 return 错误响应时,它会转到成功回调(而不是像我预期的那样进入错误回调)并且事件是 typeof 对象(这在错误响应的反应中是无用的) .事件对象如下所示: {type:0}.
问题:
-如何正确处理httpErrorResponse (403 Forbidden) in HttpInterceptor 当我需要刷新 accessToken 并重试 http 请求时?
import {
HttpInterceptor,
HttpRequest,
HttpResponse,
HttpHandler,
HttpEvent
} from '@angular/common/http';
import 'rxjs/add/operator/map';
@Injectable()
class JWTInterceptor implements HttpInterceptor {
constructor(private tokenService: TokenService) {}
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
let myHeaders = req.headers;
if (this.tokenService.accessToken) {
myHeaders = myHeaders.append('Authorization',`${this.tokenService.accessToken.token_type} ${this.tokenService.accessToken.access_token}`)
}
const authReq = req.clone({headers: myHeaders});
return next.handle(authReq).map((event: HttpEvent<any>) => {
if (event instanceof HttpResponse) {
// success callback
}
}, (err: any) => {
if (err instanceof HttpErrorResponse {
if (err.status === 403) {
// error callback
this.tokenService.obtainAccessToken()
}
}
})
.retry(1);
}
}
您需要从 RxJS 添加 catch
运算符。这就是错误所在,您可以相应地处理它。
当您收到状态为 0 的错误时,这很可能意味着远程服务器已关闭并且无法建立连接。
看看我的示例逻辑:
this.http.request(url, options)
.map((res: Response) => res.json())
.catch((error: any) => {
const err = error.json();
// Refresh JWT
if (err.status === 403) {
// Add your token refresh logic here.
}
return Observable.throw(err);
});
为了让您的刷新逻辑通过拦截器,您需要 return 调用,函数也应该 return 和 Observable
。比如修改上面的原逻辑:
this.http.request(url, options)
.map((res: Response) => res.json())
.catch((error: any) => {
const err = error.json();
// Refresh JWT
if (err.status === 403) {
// refreshToken makes another HTTP call and returns an Observable.
return this.refreshToken(...);
}
return Observable.throw(err);
});
如果您希望能够重试原始请求,您可以做的是传递原始请求数据,以便在成功刷新令牌后您可以再次进行调用。例如,这就是您的 refreshToken
函数的样子:
refreshToken(url: stirng, options: RequestOptionsArgs, body: any, tokenData: any): Observable<any>
return this.post(`${this.url}/token/refresh`, tokenData)
.flatMap((res: any) => {
// This is where I retry the original request
return this.request(url, options, body);
});
}
我对这个问题的最终解决方案:
@Injectable()
export class WebApiInterceptor implements HttpInterceptor {
constructor(private tokenService: TokenService) {
}
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
console.log('*An intercepted httpRequest*', req, this.tokenService.accessToken);
const authReq = this.authenticateRequest(req);
console.log('*Updated httpRequest*', authReq);
return next.handle(authReq)
.map((event: HttpEvent<any>) => {
if (event instanceof HttpResponse) {
console.log('*An intercepted httpResponse*', event);
return event;
}
})
.catch((error: any) => {
if (error instanceof HttpErrorResponse) {
if (error.status === 403 && error.url !== environment.authEndpoint) {
return this.tokenService
.obtainAccessToken()
.flatMap((token) => {
const authReqRepeat = this.authenticateRequest(req);
console.log('*Repeating httpRequest*', authReqRepeat);
return next.handle(authReqRepeat);
});
}
} else {
return Observable.throw(error);
}
})
}
}
函数
authenticateRequest(req)
只是将授权 header 添加到原始请求的副本中
函数
obtainAccessToken()
从授权服务器获取新的令牌并存储它
只想分享对我有用的东西:
@Injectable()
export class AutoReLoginInterceptor implements HttpInterceptor {
constructor() {
}
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
// as we want to intercept the possible errors, instead of directly returning the request execution, we return an Observable to control EVERYTHING
return new Observable<HttpEvent<any>>(subscriber => {
// first try for the request
next.handle(req)
.subscribe((event: HttpEvent<any>) => {
if (event instanceof HttpResponse) {
// the request went well and we have valid response
// give response to user and complete the subscription
subscriber.next(event);
subscriber.complete();
}
},
error => {
if (error instanceof HttpErrorResponse && error.status === 401) {
console.log('401 error, trying to re-login');
// try to re-log the user
this.reLogin().subscribe(authToken => {
// re-login successful -> create new headers with the new auth token
let newRequest = req.clone({
headers: req.headers.set('Authorization', authToken)
});
// retry the request with the new token
next.handle(newRequest)
.subscribe(newEvent => {
if (newEvent instanceof HttpResponse) {
// the second try went well and we have valid response
// give response to user and complete the subscription
subscriber.next(newEvent);
subscriber.complete();
}
}, error => {
// second try went wrong -> throw error to subscriber
subscriber.error(error);
});
});
} else {
// the error was not related to auth token -> throw error to subscriber
subscriber.error(error);
}
});
});
}
/**
* Try to re-login the user.
*/
private reLogin(): Observable<string> {
// obtain new authorization token and return it
}
}
import { Injectable } from '@angular/core';
import { HttpRequest, HttpHandler, HttpEvent, HttpInterceptor } from '@angular/common/http';
import { Observable } from 'rxjs';
@Injectable()
export class JwtInterceptor implements HttpInterceptor {
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
// add authorization header with jwt token if available
const currentUser = JSON.parse(sessionStorage.getItem('currentUser'));
console.log('Interceter called');
console.log(currentUser);
// const re = 'https://newsapi.org';
const re = '/user';
if (request.url.search(re) === -1) {
if (currentUser && currentUser.token) {
console.log('Token is being added....!!!!!');
// console.log(currentUser.token);
request = request.clone({
setHeaders: {
Authorisation: `Token ${currentUser.token}`,
}
});
}
console.log('Request Sent :');
console.log(request);
}
return next.handle(request);
}
}