因此,如果令牌使用刷新令牌过期,我想续签访问令牌,这就是我的令牌拦截器的样子:
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
方法都将引发错误并多次请求新令牌。如何处理过期令牌错误?
答案 0 :(得分:0)
您可以通过使用setToken
运算符将retryWhen
连接到返回的可观察值来解决此错误行为。
这样renewToken
和setToken
不会并行执行,更重要的是{EACH请求中的拦截器链会考虑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;
}
答案 1 :(得分:0)
您需要传递新令牌。
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;
})
);
这是回调fn。
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);
})
);
}