Angular: 在拦截器中添加不记名令牌

Angular: Adding bearer token in interceptor

我正在使用 Angular + AWS Cognito

我能够登录并需要添加 Cognito 持有者令牌

@Injectable({
providedIn: 'root',
})
export class InterceptorService implements HttpInterceptor {
constructor(public loaderService: LoaderService, private router: Router) { }

intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    return this.getToken().pipe(
        mergeMap((token) => {
            request = request.clone(
                {
                    setHeaders: { Authorization: `Bearer ${token}` }
                });

            return next.handle(request);
        })
    );
}

getToken() {
    return from(
        new Promise((resolve, reject) => {
            Auth.currentSession().then((session) => {
                if (!session.isValid()) {
                    resolve(null);
                } else {
                    resolve(session.getIdToken().getJwtToken());
                }
            }).catch(err => { return resolve(null) });
        })
    );
}
}

这段代码工作正常,问题是这里如何处理HttpResponse?

我尝试了以下代码,但没有成功,请求没有 Bearer token

intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
        return next.handle(request).pipe(
            tap(
            (event) => {
                if (event instanceof HttpResponse) {
                    if (event.status !== 200) {
                        console.log('Response', event.status);
                    }
                } else {
                    return this.getToken().pipe(
                       mergeMap((token) => {
                            request = request.clone(
                                {
                                    setHeaders: { Authorization: `Bearer ${token}` }
                                });

                            return next.handle(request);
                        })
                    );
                }
            },
            (error) => {
                 // to handle errors
            }
        )
}

据我所知tap只有运行s,当时没有报错。如果你想让你的函数在出错时运行,你必须在管道中使用catchError()

tap 是产生副作用的运算符,你不能 return 从回调中观察到它,它不会被订阅。发送原始请求并在失败时使用令牌重试的逻辑也有点奇怪。我相信你应该为每个请求添加令牌。

intercept(...) {
  return this.sendWithToken(request).pipe(
    catchError(error => error.status === 401 ? this.sendWithToken(request) : throwError(error)) // try one more time if return code is 401 
  );
}

sendWithToken(request: ...) {
  return this.getToken().pipe(
    mergeMap(token => addToken(request)),
    mergeMap(authedRequest => next.handle(authedRequest)),
  );
}

还可以改进 getToken 代码,因为如果您已经有一个,则不需要创建 promise

getToken() {
    return from(
        Auth.currentSession().then((session) => {
            if (!session.isValid()) {
                return null;
            } else {
                return session.getIdToken().getJwtToken();
            }
        }).catch(err => null);
    );
}

我用以下拦截器代码解决了这个问题:

intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    return this.getToken().pipe(
        mergeMap((token) => {
            request = request.clone(
                {
                    headers: request.headers.set('Authorization', `Bearer ${token}`)
                });

            return next.handle(request).pipe(
                tap(evt => {
                      // modify here
                }),
                catchError((error: any) => {
                    if (error && error.status) {
                        if (error.status == 401) {
                            this.router.navigate(['/']);
                        }
                    } else {
                        return throwError(error);
                    }
                })
            )
        })
    );
}

getToken() {
    return from(
        Auth.currentSession().then((session) => {
            if (!session.isValid()) {
                return null;
            } else {
                return session.getIdToken().getJwtToken();
            }
        }).catch(err => null)
    );
}