我浏览了很多文章和问题,但是似乎都没有涵盖拦截器中的自动令牌刷新功能,同时还具有一个路由守卫,它等待对服务器的请求完成以验证访问令牌是否有效?
auth.guard.ts
canActivate(
route: ActivatedRouteSnapshot,
state: RouterStateSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {
return this.authService.isAuthenticated()
.pipe(
map(
res => {
if (res.success === true) {
return true;
} else {
this.authService.deleteToken();
this.router.navigateByUrl('/login');
return false;
}
}
), catchError(err => {
// NOTE: If I put the code for refresh-token request here, it works -- user stays
// logged in after getting new access token
this.authService.deleteToken();
this.router.navigate(['/login']);
return of(false);
})
);
}
auth.interceptor.ts
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
if (req.headers.get('noauth')) {
return next.handle(req.clone());
} else {
const clonedReq = req.clone({
headers: req.headers.set('Authorization', 'Bearer ' + this.authService.getToken())
});
return next.handle(clonedReq).pipe(
tap(
event => {},
err => {
if (err.error.code && err.error.code === 'EXPIRED') {
return this.handle401Error(clonedReq, next);
}
}
)
);
}
}
private handle401Error(request: HttpRequest<any>, next: HttpHandler) {
if (!this.isRefreshing) {
this.isRefreshing = true;
this.refreshTokenSubject.next(null);
return this.authService.refreshToken().pipe(
switchMap((token: any) => {
this.isRefreshing = false;
this.refreshTokenSubject.next(token.new_access_token);
return next.handle(this.addToken(request, token.new_access_token));
}),
catchError(err => {
return throwError(err);
}))
.subscribe(r => r);
} else {
return this.refreshTokenSubject.pipe(
filter(token => token != null),
take(1),
switchMap(jwt => {
return next.handle(this.addToken(request, jwt));
}));
}
}
private addToken(request: HttpRequest<any>, token: string) {
return request.clone({
setHeaders: {
'Authorization': `Bearer ${token}`
}
});
}
auth.service.ts
refreshToken() {
return this.http.post<any>(`${environment.api}/refresh-token`, {
'refreshToken': this.getRefreshToken()
}, this.noAuthHeader).pipe(tap((res) => {
if (res.success === true) {
this.setToken(res.new_access_token);
return true;
} else {
this.deleteToken();
this.router.navigateByUrl('/login');
return false;
}
}));
}
会发生什么:
我不确定这是怎么发生的,但是我在想的是获取新访问令牌的请求应该首先完成,然后身份验证请求/auth.guard应该以此为基础?
答案 0 :(得分:0)
卡住一天半后,我想我终于自己修复了:
auth.guard.ts
return this.authService.isAuthenticated()
.pipe(
map(
res => {
if (res.success === true) {
return true;
} else {
this.authService.deleteToken();
this.router.navigateByUrl('/login');
return false;
}
}
), catchError(
err => {
// Absolutely needed this handler,
// but removed the code for redirection to login and deletion of tokens
return of(false);
}
)
);
auth.interceptor.ts
return next.handle(clonedReq)
.pipe(
catchError((err: HttpErrorResponse) => {
if (err.error.code === 'EXPIRED') {
return this.handle401Error(clonedReq, next);
} else if (err.error.status === 401) {
this.router.navigateByUrl('/login');
return throwError(err.error.message);
}
// Redirect to an error landing page
return throwError(err.error.message);
})
);
private handle401Error(request: HttpRequest<any>, next: HttpHandler) {
if (!this.isRefreshing) {
this.isRefreshing = true;
this.refreshTokenSubject.next(null);
return this.authService.refreshToken()
.pipe(
switchMap((token: any) => {
this.refreshTokenSubject.next(token.new_access_token);
this.authService.setToken(token.new_access_token);
return next.handle(this.addToken(request, token.new_access_token));
}), catchError(err => {
// .subscribe() not needed!Just an error handler
if (err.error.code === 'EXPIRED') {
this.authService.deleteToken();
this.router.navigateByUrl('/login');
return throwError(err.error.message);
}
// Redirect to error landing page
return throwError(err.error.message);
}),
finalize(() => {
this.isRefreshing = false;
})
);
...
希望您对此有帮助!