在发出克隆请求之前更新令牌
Posted
技术标签:
【中文标题】在发出克隆请求之前更新令牌【英文标题】:Renew token before making cloned request 【发布时间】:2020-10-07 02:51:44 【问题描述】:所以我想在令牌过期时使用刷新令牌更新访问令牌,这就是我的令牌拦截器的样子:
intercept(req: HttpRequest<any>, next: HttpHandler)
let authservice = this.injector.get(AuthService)
let tokenreq = req.clone(
setHeaders:
Authorization: `Bearer $authservice.setToken()`
)
return next.handle(tokenreq).pipe(
catchError((error) =>
if (error.error.message == 'The incoming token has expired')
authservice.renewToken()
let tokenreq = req.clone(
setHeaders:
Authorization: `Bearer $authservice.setToken()`
)
return next.handle(tokenreq)
else return throwError(error)
)
);
问题是在我收到过期令牌消息后authservice.renewToken()
和authservice.setToken()
被同时调用,所以再次设置了过期的令牌。
另一个问题是,如果用户使用 cookie 中过期的令牌再次打开应用程序,所有GET
方法都会抛出错误并多次请求新令牌。如何处理过期令牌错误?
【问题讨论】:
Renewtoken 和 settoken 是同步方法吧? 我想是的。我还没有声明它们是异步的 你能显示renewToken
的代码吗?
你能用github.com/softonic/axios-retry吗?
renewToken 方法到底是做什么的?
【参考方案1】:
您需要传递新令牌。
return next.handle(request).pipe(
map((event: HttpEvent<any>) =>
return this.eventCallSendBack(event);
),
catchError((error: HttpErrorResponse) =>
if (error instanceof HttpErrorResponse)
switch (error.status)
case 401:
return this.handle401Error(request, next);
case 403:
//DO Want you want here.
throw error;
default:
throw error;
throw error;
)
);
这是回调函数。
private eventCallSendBack(event)
if (event instanceof HttpResponse)
return event.clone( body: event.body );
return event;
您想拥有并添加令牌 fn:
private addToken(event: HttpRequest<any>, token: string)
return event.clone(
setHeaders:
Authorization: `Bearer $token`
);
现在假设您的令牌已过期,您需要创建 fn 以从某个 Auth 中获取新令牌。
private handle401Error(
request: HttpRequest<any>,
next: HttpHandler
): Observable<any>
return authServices
.refreshToken(
token //This is the old token to get the new.
)
.pipe(
map(jwt =>
return jwt;
),
switchMap((jwt: any) =>
return next
.handle(this.addToken(request, jwt.token)) // Add token in responce
.pipe(
map((event: HttpEvent<any>) =>
return this.eventCallSendBack(event);
)
);
),
catchError(err =>
return throwError(err);
)
);
【讨论】:
【参考方案2】:您可以通过使用retryWhen
运算符将setToken
连接到返回的observable 来修复此错误行为。
这样renewToken
和setToken
将不会并行执行,更重要的是setToken
将被每个请求中的拦截器链考虑在内。
intercept(req: HttpRequest<any>, next: HttpHandler)
const authservice = this.injector.get(AuthService);
return of(req).pipe(
switchMap((req) =>
return authservice.setToken() // Gets the token. *should rename this method to getToken()
.pipe(
map(token => // Set the token.
const tokenreq = req.clone(
setHeaders:
Authorization: `Bearer $authservice.setToken()`
);
return tokenreq;
)
),
switchMap(tokenreq => next.handle(tokenreq)), // Execute next interceptor and eventually send the request.
retryWhen(errors => errors.pipe(
mergeMap((err: HttpErrorResponse, i: number) =>
authservice.invalidateToken() // Invalidate token. Erase token or expires it.
if (error.error.message == 'The incoming token has expired')
return of(err); // will start the current pipe all over again - and get the token once again.
return throwError(error);
)
)
)
说明:
在问题中,setToken
没有连接到拦截器方法返回的可观察链。
之前的代码执行流程:
1) interceptor method execution -> setToken() -> return observable
2) request asked by http.get(..) -> chain of observables return by interceptors -> request sent -> chain of observables
在这个答案中:
1) interceptor method execution -> setToken() -> return observable
2) request asked by http.get(..) -> chain of observables return by interceptors and setToken() inside one! -> request sent -> chain of observables
注意:
setToken
方法应该返回一个带有令牌的 observable,invalidateToken
应该能够删除令牌。
这可以通过以下方式轻松实现:
private token$: AsyncSubject<string>;
getToken(): Observable
if (!token$) this.token$ = new AsyncSubject();
getTokenOperation.subscribe(t =>
this.token$.next(t);
this.token$.complete();
)
return this.token$.asObservable();
invalidateToken()
this.token$ = null;
【讨论】:
以上是关于在发出克隆请求之前更新令牌的主要内容,如果未能解决你的问题,请参考以下文章
以管理员身份使用自定义令牌向 FB DB 发出 REST 请求
使用过期令牌发出同时 API 请求时如何避免多个令牌刷新请求