Angular 4.3 - HTTP 拦截器 - 刷新 JWT 令牌
Posted
技术标签:
【中文标题】Angular 4.3 - HTTP 拦截器 - 刷新 JWT 令牌【英文标题】:Angular 4.3 - HTTP Interceptor - refresh JWT token 【发布时间】:2017-12-31 09:34:40 【问题描述】:我需要(在拦截器类中)对 403 Forbidden HTTP status(获取/刷新)JWT 令牌做出反应,并使用新令牌重试请求。
在下面的代码中,当服务器返回错误响应时,它会进入成功回调(而不是像我期望的那样进入错误回调),并且事件是 typeof 对象(对错误响应的反应是无用的)。事件对象如下所示: 类型:0。
问题:
-如何正确处理httpErrorResponse(403 Forbidden)中的 当我需要刷新accessToken并重试http请求时使用HttpInterceptor?
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);
【问题讨论】:
【参考方案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);
);
为了让你的刷新逻辑通过拦截器,你需要返回调用,函数也应该返回一个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);
);
【讨论】:
它解决了我的部分问题——在 catch 中,我现在可以访问 HttpErrorResponse 并调用我的刷新/获取令牌逻辑——谢谢。但是当我调用 .retry(1) 时,新的 http 请求不会再次通过拦截器(因此 JWT 标头不会添加到新调用中) 您需要在刷新函数中返回一个Observable
。然后在该 catch 块中返回该刷新函数的调用,它将添加到拦截器中。例如更新的答案。【参考方案2】:
我对这个问题的最终解决方案:
@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)
只需将授权标头添加到原始请求的副本中
功能
obtainAccessToken()
获取新的令牌形式授权服务器并存储它
【讨论】:
【参考方案3】:只是想分享对我有用的东西:
@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
【讨论】:
完美。谢谢。授权标头应包含 Bearer 前缀。【参考方案4】: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);
【讨论】:
以上是关于Angular 4.3 - HTTP 拦截器 - 刷新 JWT 令牌的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 Angular cli 升级 Angular 的次要版本
Angular - http 拦截器 - http 速率限制器 - 滑动窗口
单元测试 Angular 12 HTTP 拦截器 expectOne 失败