Angular 8 在身份验证完成之前抑制 HTTP 调用

Angular 8 suppress HTTP calls before authentication is complete

所以我有一个 API 调用我页面的页眉部分,它是 app.component.html 的一部分。然后我的路由模块中有几个屏幕有 canActivate。只有路由到这些屏幕才能触发 auth.guard,然后进行身份验证。但由于此菜单代码在 app.component.html 内。甚至在我有令牌之前就已经进行了调用,并且我在 console.log 中出现了 401 错误。有没有办法在身份验证结束之前阻止此调用,即我有刷新、ID 和访问令牌?

app-routing module

const routes: Routes = [
    { path: 'abc', component:abcComponent , canActivate: [AuthGuard] },
    {path: 'xyz', component:xyzComponent , canActivate: [AuthGuard] }
];

app.component.ts

ngOnIt(){
    this.httpService.getMenu().subscribe((response) => {
        this.menuArray=response
    });
}
export class TokenInterceptorService implements HttpInterceptor {
    private isRefreshing = false;
    private refreshTokenSubject: BehaviorSubject<any> = new BehaviorSubject<any>(null);

    constructor(private injector: Injector) {}
    intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
        if (req.url == environment.authTokenPostUrl) {
            const tokenreq = req.clone({});
            return next.handle(tokenreq);
        } else {
            const tokenreq = this.addToken(req);

            return next.handle(tokenreq).pipe(
                catchError((error) => {
                    const cookie = this.injector.get(CookieService);
                    if (error.status === 401) {
                        return this.handle401Error(tokenreq, next);
                    } else {
                        return throwError(error);
                    }
                })
            );
        }
    }

    private addToken(request: HttpRequest<any>) {
        const authservice = this.injector.get(AuthService);
        const headers = { Authorization: `Bearer ${authservice.setIdToken()}` };
        return request.clone({
            setHeaders: headers,
        });
    }

    private handle401Error(request: HttpRequest<any>, next: HttpHandler) {
        if (!this.isRefreshing) {
            const authservice = this.injector.get(AuthService);
            this.isRefreshing = true;
            this.refreshTokenSubject.next(null);

            return authservice.refreshToken().pipe(
                switchMap((token: any) => {
                    this.isRefreshing = false;
                    this.refreshTokenSubject.next(token.id_token);
                    return next.handle(this.addToken(request));
                })
            );
        } else {
            return this.refreshTokenSubject.pipe(
                filter((token) => token != null),
                take(1),
                switchMap((jwt) => {
                    return next.handle(this.addToken(request));
                })
            );
        }
    }
}


所以我尝试了

ngOnIt(){
    if(this.cookieService.get('refresh_token'))
        this.httpService.getMenu().subscribe((response) => {
            this.menuArray=response
        });
    }
}

但是这个逻辑不起作用,因为身份验证是在触发此调用之后发生的。

有没有办法解决这个问题?

虽然 RxJS 解决方案可能更优雅(我建议您深入研究),但我将逆流而行,提供更“old-school-angular”的解决方案。它使用简单的属性,看不到 $ 符号。对于经验不足的程序员来说,这可能是一个不错的选择,同时保持了可维护性。

在您的 AuthService 中,创建一个名为 authenticated 的 属性。验证后将其设置为 true

class AuthService {
    authenticated = false;

    authenticate(params) {
      this.http.post([authentication stuff]).subscribe(() => {
        // do what you do now
        this.authenticated = true;
   }
}

然后,在单独的 <app-menu> 组件中分离菜单代码。

class MenuComponent {
  ngOnInit() {
    this.httpService.getMenu().subscribe((response) => {
        this.menuArray=response
    });
  }
}

在主要 AppComponent 中,link 身份验证服务和代理 authenticated 属性 通过 getter。

class AppComponent {
  get authenticated() { return this.authService.authenticated; }
  constructor(authService: AuthService) {}
}

然后,在 AppComponent 的模板中使用 *ngIf 显示菜单组件。

<app-menu *ngIf="authenticated"></app-menu>

另一种方法是使用 authenticated$ BehaviourSubject,但我会把它留给其他答案。