【发布时间】:2020-02-01 12:28:07
【问题描述】:
在我的应用程序中,我使用 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 请求,以从 API 端点检索新的 JWT 令牌,该令牌应用于在继续原始请求之前覆盖旧令牌。
我试图弄清楚如何在我的 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;
}
【问题讨论】:
-
为什么要在发送前检查令牌?客户端应用程序应该是最愚蠢的,你所有的逻辑都应该在后端。 API 有多种方法来验证令牌或刷新它。只需发送令牌,让它处理它?
-
你在找itnext.io/…
-
您的 Angular 代码不应检查 JWT 令牌的有效性。后端服务是否负责该任务,返回错误(可能是 401 Unauthorized)。然后,Angular 应用程序应该重定向到登录组件或调用将提供另一个令牌的授权服务。顺便说一句,你试过@auth0/angular-jwt 吗?很容易添加到您的应用程序中,您将不再需要拦截器。