Angular 7 自动刷新不记名令牌
Angular 7 auto-refresh bearer token
我有一个 AuthService,基本上有两种方法:
getAuthToken
(returns Promise,因此可以延迟调用/多次调用,并在单个集合上进行阻塞等待)
refreshToken
(也是returns一个Promise,使用原始JWT上可用的刷新令牌来请求新的授权令牌)
我想自动
- 将持有者令牌应用于每个 http 请求(有效)
- 在刷新时刷新令牌——我快到了,只是使用刷新令牌的请求结果没有返回给原始订阅者。
代码如下:
import { HttpEvent, HttpHandler, HttpHeaders, HttpInterceptor, HttpRequest } from "@angular/common/http";
import { from, Observable } from "rxjs";
import { Injectable } from "@angular/core";
import { AuthService } from "./auth.service";
@Injectable()
export class AuthHttpInterceptor implements HttpInterceptor {
constructor(
private _authService: AuthService,
) {
}
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
return from(this.addBearerToken(req, next));
}
private async addBearerToken(req: HttpRequest<any>, next: HttpHandler): Promise<HttpEvent<any>> {
const token = await this._authService.getAuthToken();
const headerSettings = req.headers.keys().reduce(
(acc, cur) => {
acc[cur] = req.headers.getAll(cur);
return acc;
}, {});
if (token) {
headerSettings["Authorization"] = `Bearer ${ token }`;
} else {
console.log("performing request without auth!");
}
// prevent 302 redirect to challenge on a 401
headerSettings["X-Requested-With"] = "XMLHttpRequest";
const
headers = new HttpHeaders(headerSettings),
newRequest = req.clone({ headers });
const result = next.handle(newRequest).toPromise();
result.catch(async (err) => {
if (err.status === 401) {
const
newToken = await this._authService.refreshToken();
headerSettings["Authorization"] = `Bearer ${ newToken }`;
const
updatedHeaders = new HttpHeaders(headerSettings),
updatedRequest = req.clone({ headers: updatedHeaders });
console.log("requery with new token"); // <-- I see this when I have a 401, eg by altering the auth token to be bad, whilst leaving the refresh token alone
return next.handle(updatedRequest).toPromise().then(data => {
console.log("requeried data:", data); // <-- I also see this fire, with the valid data coming back from the second request
return data; // <-- however the original caller doesn't get this data
});
}
});
return result;
}
}
我不得不假设这可能是由于我混合了 Observables 和 Promises(我这样做是因为 AuthService 是异步的,使用 Promises)。此外,如果没有 401,则原始调用确实会正确通过——就好像承诺链只是在行
之后被丢弃一样
next.handle(newRequest).toPromise();
我错过了什么?
你在这里打破了链条:
const result = next.handle(newRequest).toPromise();
result.catch(async (err) => {
...
});
return result;
result.catch
returns 一个新的 Promise,您的处理程序将不会等待您在 catch
.
中调用的所有操作
所以可以这样写:
const result = next.handle(newRequest).toPromise();
return result.catch(async (err) => {
...
});
另外,您可能想要做的是 在一个进程中不要多次调用 refreshToken
cachedRequest: Promise<any>; // define prop in your class
...
if (!this.cachedRequest) {
this.cachedRequest = this.authService.refreshToken();
}
const newToken = await this.cachedRequest;
this.cachedRequest = null;
这里有一个 Simple Demo 你可以测试一下。 (我在那里处理 404 但没关系)
我有一个 AuthService,基本上有两种方法:
getAuthToken
(returns Promise,因此可以延迟调用/多次调用,并在单个集合上进行阻塞等待)refreshToken
(也是returns一个Promise,使用原始JWT上可用的刷新令牌来请求新的授权令牌)
我想自动
- 将持有者令牌应用于每个 http 请求(有效)
- 在刷新时刷新令牌——我快到了,只是使用刷新令牌的请求结果没有返回给原始订阅者。
代码如下:
import { HttpEvent, HttpHandler, HttpHeaders, HttpInterceptor, HttpRequest } from "@angular/common/http";
import { from, Observable } from "rxjs";
import { Injectable } from "@angular/core";
import { AuthService } from "./auth.service";
@Injectable()
export class AuthHttpInterceptor implements HttpInterceptor {
constructor(
private _authService: AuthService,
) {
}
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
return from(this.addBearerToken(req, next));
}
private async addBearerToken(req: HttpRequest<any>, next: HttpHandler): Promise<HttpEvent<any>> {
const token = await this._authService.getAuthToken();
const headerSettings = req.headers.keys().reduce(
(acc, cur) => {
acc[cur] = req.headers.getAll(cur);
return acc;
}, {});
if (token) {
headerSettings["Authorization"] = `Bearer ${ token }`;
} else {
console.log("performing request without auth!");
}
// prevent 302 redirect to challenge on a 401
headerSettings["X-Requested-With"] = "XMLHttpRequest";
const
headers = new HttpHeaders(headerSettings),
newRequest = req.clone({ headers });
const result = next.handle(newRequest).toPromise();
result.catch(async (err) => {
if (err.status === 401) {
const
newToken = await this._authService.refreshToken();
headerSettings["Authorization"] = `Bearer ${ newToken }`;
const
updatedHeaders = new HttpHeaders(headerSettings),
updatedRequest = req.clone({ headers: updatedHeaders });
console.log("requery with new token"); // <-- I see this when I have a 401, eg by altering the auth token to be bad, whilst leaving the refresh token alone
return next.handle(updatedRequest).toPromise().then(data => {
console.log("requeried data:", data); // <-- I also see this fire, with the valid data coming back from the second request
return data; // <-- however the original caller doesn't get this data
});
}
});
return result;
}
}
我不得不假设这可能是由于我混合了 Observables 和 Promises(我这样做是因为 AuthService 是异步的,使用 Promises)。此外,如果没有 401,则原始调用确实会正确通过——就好像承诺链只是在行
之后被丢弃一样next.handle(newRequest).toPromise();
我错过了什么?
你在这里打破了链条:
const result = next.handle(newRequest).toPromise();
result.catch(async (err) => {
...
});
return result;
result.catch
returns 一个新的 Promise,您的处理程序将不会等待您在 catch
.
所以可以这样写:
const result = next.handle(newRequest).toPromise();
return result.catch(async (err) => {
...
});
另外,您可能想要做的是 在一个进程中不要多次调用 refreshToken
cachedRequest: Promise<any>; // define prop in your class
...
if (!this.cachedRequest) {
this.cachedRequest = this.authService.refreshToken();
}
const newToken = await this.cachedRequest;
this.cachedRequest = null;
这里有一个 Simple Demo 你可以测试一下。 (我在那里处理 404 但没关系)