Angular 8 拦截调用刷新令牌

Angular 8 Intercept call to refresh token

如果当前访问令牌已过期,我正在尝试刷新访问令牌。

我一次发送多个请求,我想做一个队列,这样其他请求就不会请求刷新令牌路由。

我用谷歌搜索了一些最佳实践和示例,并找到了以下针对 Angular 6 和 rxjs v6 的解决方案,它使用了 BehaviourSubject 和 switchMaps。 (请看附件代码)

但是我使用的是 Angular 8 (8.1) 和 rxjs v6.4,这个解决方案对我不起作用。

它在 this.authService.requestAccessToken().pipe 中根本没有达到 switchMap。 (使用 console.log 测试)

然而,如果我评论 return this.refreshTokenSubject.pipe 和 return next.handle(request) 它到达那个 switchMap,但我的其他请求都失败了。

您知道是否有任何更改,或者我应该尝试以其他方式执行此操作吗?

    import { Injectable } from '@angular/core';
    import { HttpRequest, HttpHandler, HttpEvent, HttpInterceptor } from '@angular/common/http';
    import { AuthService } from './auth.service';
    import { Observable, BehaviorSubject, Subject } from 'rxjs';
    import { switchMap, take, filter } from 'rxjs/operators';
    @Injectable()
    export class TokenInterceptor implements HttpInterceptor {
        private refreshTokenInProgress = false;
        private refreshTokenSubject: Subject<any> = new BehaviorSubject<any>(null);

        constructor(public authService: AuthService) { }
        intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {

            const accessExpired = this.authService.isAccessTokenExpired();
            const refreshExpired = this.authService.isRefreshTokenExpired();

            if (accessExpired && refreshExpired) {
                return next.handle(request);
            }
            if (accessExpired && !refreshExpired) {
                if (!this.refreshTokenInProgress) {
                    this.refreshTokenInProgress = true;
                    this.refreshTokenSubject.next(null);
                    return this.authService.requestAccessToken().pipe(
                        switchMap((authResponse) => {
                            this.authService.saveToken(AuthService.TOKEN_NAME, authResponse.accessToken);
                            this.authService.saveToken(AuthService.REFRESH_TOKEN_NAME, authResponse.refreshToken);
                            this.refreshTokenInProgress = false;
                            this.refreshTokenSubject.next(authResponse.refreshToken);
                            return next.handle(this.injectToken(request));
                        }),
                    );
                } else {
                    return this.refreshTokenSubject.pipe(
                        filter(result => result !== null),
                        take(1),
                        switchMap((res) => {
                            return next.handle(this.injectToken(request))
                        })
                    );
                }
            }

            if (!accessExpired) {
                return next.handle(this.injectToken(request));
            }
        }

        injectToken(request: HttpRequest<any>) {
            const token = this.authService.getToken(AuthService.TOKEN_NAME);
            return request.clone({
                setHeaders: {
                    Authorization: `Bearer ${token}`
                }
            });
        }
    }
    requestAccessToken(): Observable<any> {
        const refreshToken = this.getToken(AuthService.REFRESH_TOKEN_NAME);
        return this.http.post(`${this.basePath}/auth/refresh`, { refreshToken });
    }

更新 1

所以我使用这些资源来编写我的拦截器:

更新 2

我已经从拦截器范围中排除了 refresh 请求,现在它正在运行 感谢@JBNizet

我已经从拦截器范围中排除了刷新请求,现在它可以正常工作了。 我进行了临时修复,以确保它以最快的方式工作。

现在我的 TokenInterceptor 看起来像:

import { Injectable } from '@angular/core';
import { HttpRequest, HttpHandler, HttpEvent, HttpInterceptor } from '@angular/common/http';
import { AuthService } from './auth.service';
import { Observable, BehaviorSubject, Subject } from 'rxjs';
import { switchMap, take, filter } from 'rxjs/operators';
@Injectable()
export class TokenInterceptor implements HttpInterceptor {
    private refreshTokenInProgress = false;
    private refreshTokenSubject: Subject<any> = new BehaviorSubject<any>(null);

    constructor(public authService: AuthService) { }
    intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
        if (request.url.indexOf('refresh') !== -1) {
            return next.handle(request);
        }

        const accessExpired = this.authService.isAccessTokenExpired();
        const refreshExpired = this.authService.isRefreshTokenExpired();

        if (accessExpired && refreshExpired) {
            return next.handle(request);
        }
        if (accessExpired && !refreshExpired) {
            if (!this.refreshTokenInProgress) {
                this.refreshTokenInProgress = true;
                this.refreshTokenSubject.next(null);
                return this.authService.requestAccessToken().pipe(
                    switchMap((authResponse) => {
                        this.authService.saveToken(AuthService.TOKEN_NAME, authResponse.accessToken);
                        this.authService.saveToken(AuthService.REFRESH_TOKEN_NAME, authResponse.refreshToken);
                        this.refreshTokenInProgress = false;
                        this.refreshTokenSubject.next(authResponse.refreshToken);
                        return next.handle(this.injectToken(request));
                    }),
                );
            } else {
                return this.refreshTokenSubject.pipe(
                    filter(result => result !== null),
                    take(1),
                    switchMap((res) => {
                        return next.handle(this.injectToken(request))
                    })
                );
            }
        }

        if (!accessExpired) {
            return next.handle(this.injectToken(request));
        }
    }

    injectToken(request: HttpRequest<any>) {
        const token = this.authService.getToken(AuthService.TOKEN_NAME);
        return request.clone({
            setHeaders: {
                Authorization: `Bearer ${token}`
            }
        });
    }
}

感谢@JBNizet

@AntGrisha,感谢您在 correcting.Saved 我辛苦工作后发布解决方案。我添加了错误处理,因为 well.Might 可以帮助像我这样的初学者,所以发布它 here.I 使用了 angular 9.1.5 版本。

应该如下所示在请求处理程序中进行管道传输。

return next.handle(this.injectToken(req)).pipe(
            catchError(this.handleError)

错误处理函数定义

injectToken(request: HttpRequest<any>) {
    const token = this.authService.getToken(AuthService.TOKEN_NAME);
    return request.clone({
        setHeaders: {
            Authorization: `Bearer ${token}`
        }
    });
}
//Error handling function
handleError(error: HttpErrorResponse){
console.log('Error Occurred: '+error);
if(error.status==401){
  return throwError("Unauthorized!");
}
else{
return throwError(error);
 }