在发出克隆请求之前更新令牌
Renew token before making cloned request
因此,如果令牌已过期,我想使用刷新令牌更新访问令牌,这就是我的令牌拦截器的样子:
intercept(req: HttpRequest<any>, next: HttpHandler) {
let authservice = this.injector.get(AuthService)
let tokenreq = req.clone({
setHeaders: {
Authorization: `Bearer ${authservice.setToken()}`
}
})
return next.handle(tokenreq).pipe(
catchError((error) => {
if (error.error.message == 'The incoming token has expired') {
authservice.renewToken()
let tokenreq = req.clone({
setHeaders: {
Authorization: `Bearer ${authservice.setToken()}`
}
})
return next.handle(tokenreq)
}
else return throwError(error)
})
);
}
}
问题是在我收到过期令牌消息后 authservice.renewToken()
和 authservice.setToken()
被同时调用,所以过期的令牌被再次设置。
另一个问题是,如果用户使用 cookie 中的过期令牌再次打开应用程序,所有 GET
方法将抛出错误并多次请求新令牌。如何处理过期令牌错误?
您可以通过使用 retryWhen
运算符将 setToken
连接到 returned 可观察对象来修复此错误行为。
这样 renewToken
和 setToken
将不会并行执行,更重要的是 setToken
将在每个请求中被拦截器链考虑。
intercept(req: HttpRequest<any>, next: HttpHandler) {
const authservice = this.injector.get(AuthService);
return of(req).pipe(
switchMap((req) => {
return authservice.setToken() // Gets the token. *should rename this method to getToken()
.pipe(
map(token => { // Set the token.
const tokenreq = req.clone({
setHeaders: {
Authorization: `Bearer ${authservice.setToken()}`
}
});
return tokenreq;
})
),
switchMap(tokenreq => next.handle(tokenreq)), // Execute next interceptor and eventually send the request.
retryWhen(errors => errors.pipe(
mergeMap((err: HttpErrorResponse, i: number) => {
authservice.invalidateToken() // Invalidate token. Erase token or expires it.
if (error.error.message == 'The incoming token has expired') {
return of(err); // will start the current pipe all over again - and get the token once again.
}
return throwError(error);
})
)
)
}
解释:
在问题中,setToken
未连接到由拦截器方法 return 编辑的可观察链。
之前的代码执行流程:
1) interceptor method execution -> setToken() -> return observable
2) request asked by http.get(..) -> chain of observables return by interceptors -> request sent -> chain of observables
在这个回答中:
1) interceptor method execution -> setToken() -> return observable
2) request asked by http.get(..) -> chain of observables return by interceptors and setToken() inside one! -> request sent -> chain of observables
注:
setToken
方法应该 return 一个带有令牌的可观察对象并且 invalidateToken
应该能够删除令牌。
这可以通过以下方式轻松实现:
private token$: AsyncSubject<string>;
getToken(): Observable {
if (!token$) this.token$ = new AsyncSubject();
getTokenOperation.subscribe(t => {
this.token$.next(t);
this.token$.complete();
})
return this.token$.asObservable();
}
invalidateToken() {
this.token$ = null;
}
您需要传递新令牌。
return next.handle(request).pipe(
map((event: HttpEvent<any>) => {
return this.eventCallSendBack(event);
}),
catchError((error: HttpErrorResponse) => {
if (error instanceof HttpErrorResponse) {
switch (error.status) {
case 401:
return this.handle401Error(request, next);
case 403:
//DO Want you want here.
throw error;
default:
throw error;
}
}
throw error;
})
);
这是回调 fn。
private eventCallSendBack(event) {
if (event instanceof HttpResponse) {
return event.clone({ body: event.body });
}
return event;
}
您想拥有一个添加令牌 fn:
private addToken(event: HttpRequest<any>, token: string) {
return event.clone({
setHeaders: {
Authorization: `Bearer ${token}`
}
});
}
现在假设您的令牌已过期,您需要创建 fn 以从某些 Auth 获取新令牌。
private handle401Error(
request: HttpRequest<any>,
next: HttpHandler
): Observable<any> {
return authServices
.refreshToken(
token //This is the old token to get the new.
)
.pipe(
map(jwt => {
return jwt;
}),
switchMap((jwt: any) => {
return next
.handle(this.addToken(request, jwt.token)) // Add token in responce
.pipe(
map((event: HttpEvent<any>) => {
return this.eventCallSendBack(event);
})
);
}),
catchError(err => {
return throwError(err);
})
);
}
因此,如果令牌已过期,我想使用刷新令牌更新访问令牌,这就是我的令牌拦截器的样子:
intercept(req: HttpRequest<any>, next: HttpHandler) {
let authservice = this.injector.get(AuthService)
let tokenreq = req.clone({
setHeaders: {
Authorization: `Bearer ${authservice.setToken()}`
}
})
return next.handle(tokenreq).pipe(
catchError((error) => {
if (error.error.message == 'The incoming token has expired') {
authservice.renewToken()
let tokenreq = req.clone({
setHeaders: {
Authorization: `Bearer ${authservice.setToken()}`
}
})
return next.handle(tokenreq)
}
else return throwError(error)
})
);
}
}
问题是在我收到过期令牌消息后 authservice.renewToken()
和 authservice.setToken()
被同时调用,所以过期的令牌被再次设置。
另一个问题是,如果用户使用 cookie 中的过期令牌再次打开应用程序,所有 GET
方法将抛出错误并多次请求新令牌。如何处理过期令牌错误?
您可以通过使用 retryWhen
运算符将 setToken
连接到 returned 可观察对象来修复此错误行为。
这样 renewToken
和 setToken
将不会并行执行,更重要的是 setToken
将在每个请求中被拦截器链考虑。
intercept(req: HttpRequest<any>, next: HttpHandler) {
const authservice = this.injector.get(AuthService);
return of(req).pipe(
switchMap((req) => {
return authservice.setToken() // Gets the token. *should rename this method to getToken()
.pipe(
map(token => { // Set the token.
const tokenreq = req.clone({
setHeaders: {
Authorization: `Bearer ${authservice.setToken()}`
}
});
return tokenreq;
})
),
switchMap(tokenreq => next.handle(tokenreq)), // Execute next interceptor and eventually send the request.
retryWhen(errors => errors.pipe(
mergeMap((err: HttpErrorResponse, i: number) => {
authservice.invalidateToken() // Invalidate token. Erase token or expires it.
if (error.error.message == 'The incoming token has expired') {
return of(err); // will start the current pipe all over again - and get the token once again.
}
return throwError(error);
})
)
)
}
解释:
在问题中,setToken
未连接到由拦截器方法 return 编辑的可观察链。
之前的代码执行流程:
1) interceptor method execution -> setToken() -> return observable
2) request asked by http.get(..) -> chain of observables return by interceptors -> request sent -> chain of observables
在这个回答中:
1) interceptor method execution -> setToken() -> return observable
2) request asked by http.get(..) -> chain of observables return by interceptors and setToken() inside one! -> request sent -> chain of observables
注:
setToken
方法应该 return 一个带有令牌的可观察对象并且 invalidateToken
应该能够删除令牌。
这可以通过以下方式轻松实现:
private token$: AsyncSubject<string>;
getToken(): Observable {
if (!token$) this.token$ = new AsyncSubject();
getTokenOperation.subscribe(t => {
this.token$.next(t);
this.token$.complete();
})
return this.token$.asObservable();
}
invalidateToken() {
this.token$ = null;
}
您需要传递新令牌。
return next.handle(request).pipe(
map((event: HttpEvent<any>) => {
return this.eventCallSendBack(event);
}),
catchError((error: HttpErrorResponse) => {
if (error instanceof HttpErrorResponse) {
switch (error.status) {
case 401:
return this.handle401Error(request, next);
case 403:
//DO Want you want here.
throw error;
default:
throw error;
}
}
throw error;
})
);
这是回调 fn。
private eventCallSendBack(event) {
if (event instanceof HttpResponse) {
return event.clone({ body: event.body });
}
return event;
}
您想拥有一个添加令牌 fn:
private addToken(event: HttpRequest<any>, token: string) {
return event.clone({
setHeaders: {
Authorization: `Bearer ${token}`
}
});
}
现在假设您的令牌已过期,您需要创建 fn 以从某些 Auth 获取新令牌。
private handle401Error(
request: HttpRequest<any>,
next: HttpHandler
): Observable<any> {
return authServices
.refreshToken(
token //This is the old token to get the new.
)
.pipe(
map(jwt => {
return jwt;
}),
switchMap((jwt: any) => {
return next
.handle(this.addToken(request, jwt.token)) // Add token in responce
.pipe(
map((event: HttpEvent<any>) => {
return this.eventCallSendBack(event);
})
);
}),
catchError(err => {
return throwError(err);
})
);
}