angular - Angular Auth拦截器无法刷新令牌 - Angular 7
问题描述
如果刷新令牌过期,我会尝试刷新访问令牌。登录后,我收到了两个令牌并将它们存储在我的本地存储中。在服务器以状态 401 响应后,我尝试发送刷新令牌但它失败了,似乎拦截器在标头内发送了 berer 访问令牌而不是刷新令牌
授权拦截器
@Injectable()
export class AuthInterceptor implements HttpInterceptor {
private isRefreshing = false;
private refreshTokenSubject: BehaviorSubject<any> = new BehaviorSubject<any>(null);
constructor(private authService: LoginService,
private uploadService: ContractUploadService ) { }
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<any> {
if (this.authService.getJwtAccessToken()) {
request = this.addToken(request, this.authService.getJwtAccessToken());
}
return next.handle(request).pipe(catchError(error => {
if (error instanceof HttpErrorResponse && error.status === 401) {
return this.handle401Error(request, next);
} else {
return throwError(error);
}
}));
}
addToken(request: HttpRequest<any>, token: string) {
return request.clone({
setHeaders: {
'Authorization': `Bearer ${token}`
}
});
}
handle401Error(request: HttpRequest<any>, next: HttpHandler) {
if (!this.isRefreshing) {
this.isRefreshing = true;
this.refreshTokenSubject.next(null);
return this.authService.refreshToken().pipe(
finalize(() => this.isRefreshing = false),
switchMap((token: any) => {
if (token) {
this.refreshTokenSubject.next(token.jwt);
return next.handle(this.addToken(request, token.jwt));
}
this.authService.doLogoutUser();
this.uploadService.stopUploadStatusChecker();
}),
catchError(error => {
this.authService.doLogoutUser();
this.uploadService.stopUploadStatusChecker();
return throwError(error);
})
);
} else {
return this.refreshTokenSubject.pipe(
filter(token => token != null),
take(1),
switchMap(jwt => {
return next.handle(this.addToken(request, jwt));
}));
}
}
}
刷新令牌请求
refreshToken() {
return this.http
.post<any>(BACK_END_URL_REFRESH, {
'refresh_token': this.getRefreshToken()
})
.pipe(
tap((tokens: any) => {
this.storeJwtToken(tokens.jwt);
this.isAuthenticated = true;
}),
catchError((err: any) => {
console.log(err)
this.doLogoutUser();
return throwError(err);
})
);
}
登录
login(user: UserLogin) {
const userData = new FormData();
userData.append('user', user.name);
userData.append('pwd', user.password);
this.http.post<{ access_token: string; refresh_token: string }>(BACK_END_URL, userData).pipe(
finalize(() => this.interactionService.setSpinnerStatus.next(false)),
)
.subscribe(
response => {
if (response.access_token) {
this.isAuthenticated = true;
this.interactionService.setSnackBar('User logged in successfully', 'success');
this.storeTokens(response.refresh_token, response.access_token);
this.extractUserInfoFromToken(response.access_token);
this.router.navigate(['/user-panel']);
}
},
error => {
this.interactionService.setSnackBar('Wrong user name or password', 'error');
}
);
}
本地存储方法
private getRefreshToken() {
return localStorage.getItem(this.REFRESH_TOKEN);
}
private storeJwtToken(jwt: string) {
localStorage.setItem(this.JWT_ACCESS_TOKEN, jwt);
}
private storeTokens(refreshToken, accessToken) {
localStorage.setItem(this.REFRESH_TOKEN, refreshToken);
localStorage.setItem(this.JWT_ACCESS_TOKEN, accessToken);
}
private removeTokens() {
localStorage.removeItem(this.JWT_ACCESS_TOKEN);
localStorage.removeItem(this.REFRESH_TOKEN);
}
[编辑] 尝试实施@Abdellah ASKI 建议,但仍然无法正常工作
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<any> {
if (this.authService.getJwtAccessToken() && request.headers.get('No-Auth') !== 'true') {
request = this.addToken(request, this.authService.getJwtAccessToken());
}
return next.handle(request).pipe(catchError(error => {
if (error instanceof HttpErrorResponse && error.status === 401) {
return this.handle401Error(request, next);
} else {
return throwError(error);
}
}));
}
refreshToken() {
return this.http
.post<any>(BACK_END_URL_REFRESH, {
refresh_token: this.getRefreshToken(),
'No-Auth': 'true'
})
.pipe(
tap((tokens: any) => {
this.storeJwtToken(tokens.jwt);
this.isAuthenticated = true;
}),
catchError((err: any) => {
this.doLogoutUser();
return throwError(err);
})
);
[编辑2]
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<any> {
if (this.authService.getJwtAccessToken() && !request.headers.has('No-Auth')) {
request = this.addToken(request, this.authService.getJwtAccessToken());
}
return next.handle(request).pipe(catchError(error => {
if (error instanceof HttpErrorResponse && error.status === 401) {
return this.handle401Error(request, next);
} else {
return throwError(error);
}
}));
}
addToken(request: HttpRequest<any>, token: string) {
return request.clone({
setHeaders: {
'Authorization': `Bearer ${token}`
}
});
}
解决方案
[更新]
解决这个问题的想法是添加一个No-Auth:true
在每个请求上调用的标头,而不是使用什么来发送访问令牌。
因此,在您的代码中,您需要这样做:
在您的 refreshToken() 函数中,您需要添加一个新标头,如下所示:
refreshToken() {
return this.http
.post<any>(BACK_END_URL_REFRESH, {
'refresh_token': this.getRefreshToken(),
'No-Auth': 'true' <<-------<<-------<<-------<<-------
})
.pipe(
....
);
}
在您的 Auth 拦截器上,您需要检查此标头是否存在,您不需要添加访问令牌:
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<any> {
if (request.headers.has('No-Auth')) { <<-------<<-------<<-------
return next.handle(request);
}
if (this.authService.getJwtAccessToken()) {
request = this.addToken(request, this.authService.getJwtAccessToken());
}
....
}
推荐阅读
- java - 如何检测已接电话?
- mysql - 使用所有已配置的数据库/表设置 MySQL(主从)复制
- installshield - InstallShield "Full" Redistributable 强制互联网访问
- css - 为什么将谓词应用于 xpath 中的字符串是有效的?
- xml - 如何将 xs:occurs 属性组添加到我的元素声明中?
- jquery - SharePoint 2013 - GridView 上的 jQuery
- .net - .NET ASMX WebService 和客户端调用的外部类
- cumulocity - 在 cumulocity 托管的自定义应用程序中存储托管对象的位置
- windows - 适用于 Windows 10 的 Thingworx 客户端
- python - CVXPY 错误:“NotImplementedError:不允许严格不等式”