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,但我的其他请求都失败了。
您知道是否有任何更改,或者我应该尝试以其他方式执行此操作吗?
- 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>> {
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
requestAccessToken(): Observable<any> {
const refreshToken = this.getToken(AuthService.REFRESH_TOKEN_NAME);
return this.http.post(`${this.basePath}/auth/refresh`, { refreshToken });
}
更新 1
所以我使用这些资源来编写我的拦截器:
https://itnext.io/angular-tutorial-implement-refresh-token-with-httpinterceptor-bfa27b966f57(Angular 4 解决方案,我相信它会在 rxjs 版本上生效)
https://github.com/melcor76/interceptors/blob/master/src/app/interceptors/auth.interceptor.ts
更新 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);
}
如果当前访问令牌已过期,我正在尝试刷新访问令牌。
我一次发送多个请求,我想做一个队列,这样其他请求就不会请求刷新令牌路由。
我用谷歌搜索了一些最佳实践和示例,并找到了以下针对 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,但我的其他请求都失败了。
您知道是否有任何更改,或者我应该尝试以其他方式执行此操作吗?
- 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>> {
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
requestAccessToken(): Observable<any> {
const refreshToken = this.getToken(AuthService.REFRESH_TOKEN_NAME);
return this.http.post(`${this.basePath}/auth/refresh`, { refreshToken });
}
更新 1
所以我使用这些资源来编写我的拦截器:
https://itnext.io/angular-tutorial-implement-refresh-token-with-httpinterceptor-bfa27b966f57(Angular 4 解决方案,我相信它会在 rxjs 版本上生效)
https://github.com/melcor76/interceptors/blob/master/src/app/interceptors/auth.interceptor.ts
更新 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);
}