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
,但我会把它留给其他答案。
所以我有一个 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
,但我会把它留给其他答案。