angular - 刷新 JWT 令牌 - Angular 8
问题描述
我尝试按照以下教程在 Angular 8 中实现 JWT 刷新,但没有成功:
https://angular-academy.com/angular-jwt/#refresh-token
我创建了一个拦截器,但如果我不订阅令牌刷新不起作用。如果我订阅令牌刷新服务,它可以工作,但我必须从浏览器为整个页面充电。
我正在使用 Angular 8 + Ionic4
拦截器.ts
import { Injectable } from '@angular/core';
import {
HttpEvent,
HttpInterceptor,
HttpHandler,
HttpRequest,
HttpResponse,
HttpHeaders,
HttpErrorResponse,
HttpClient
} from '@angular/common/http';
import { Observable, from, BehaviorSubject, throwError } from 'rxjs';
import { tap, flatMap, catchError, mergeMap, switchMap, filter, take, finalize } from 'rxjs/operators';
import { environment } from '../environments/environment';
import { Router } from '@angular/router';
import { LoadingController } from '@ionic/angular';
import {NativeStorage} from '@ionic-native/native-storage/ngx';
import { SecurityService } from './services/security.service';
@Injectable()
export class APIInterceptor implements HttpInterceptor {
isLoading = false;
Token;
flagToken = false;
private isRefreshing = false;
private refreshTokenSubject: BehaviorSubject<any> = new BehaviorSubject<any>(null);
constructor(
private router: Router,
private http: HttpClient,
private loadingController: LoadingController,
private securityStorageService: SecurityService,
public nativeStorage: NativeStorage
) {
}
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
console.log('URL INTERCEPTOR', `${environment.url}/${req.url}`);
if (req.url === 'Token') {
const apiReq = req.clone({ url: `${environment.url}/${req.url}` });
return next.handle(apiReq);
} else {
return from(this.securityStorageService.getToken()).pipe(mergeMap(
(token) => {
// tslint:disable-next-line:prefer-const
let newReq = req.clone(
{
headers: req.headers.set('Authorization', token)
.set('Authorization-Type', 'JWT')
.set('Content-Type', 'application/json')
.set('Cache-Control', 'no-cache')
.set('Pragma', 'no-cache'),
url: `${environment.url}/${req.url}`
});
this.presentLoading();
return next.handle(newReq).pipe(
tap(event => {
if (event instanceof HttpResponse) {
console.log('Interceptor - HttpResponse = ' + event.status); // http response status code
this.dismissLoading();
}
}, error => {
this.dismissLoading();
// http response status code
if (error instanceof HttpErrorResponse) {
console.log('----response----');
console.error('status code:');
console.log('ERRORE CALLBACK', error);
console.error(error.status);
console.error(error.message);
console.log('--- end of response---');
if (error.status === 401) {
console.log('401');
return this.handle401Error(newReq, next);
} else {
return throwError(error);
}
}
// return Observable.throw(error);
})
);
}
));
}
}
private handle401Error(request: HttpRequest<any>, next: HttpHandler) {
console.log('ISREFRESHING VALUE', this.isRefreshing);
if (!this.isRefreshing) {
console.log('JWT IN IF SUBJECT');
this.isRefreshing = true;
this.refreshTokenSubject.next(null);
console.log('REFRESH SUBJECT NULL');
let params = {};
this.nativeStorage.getItem('refreshToken').then(
(data) => {
console.log('NATIVE STORAGE RTOKEN', data);
params = {
RToken: data
};
return this.securityStorageService.refreshToken(params).pipe(
switchMap((token: any) => {
console.log('SWITCHMAP TOKEN', token);
this.isRefreshing = false;
this.refreshTokenSubject.next(token.Token);
return next.handle(this.addToken(request, token.Token)).pipe(
tap(error => {console.log('ERRORE IN NEXT HANDLE', error)})
);
}),
catchError(err => {
console.log('ERRORE REFRESH TOKEN - LOGOUT APP', err);
return this.router.navigate(['/login']);
}),
finalize(() => {
this.isRefreshing = false;
})
)/*.subscribe(
(rs) => {console.log('TOKEN REFRESH SUBSCRIBE', rs);}
);*/
}
);
} else {
console.log('ELSE SUBJECT TOKEN');
return this.refreshTokenSubject.pipe(
filter(token => token != null),
take(1),
switchMap(jwt => {
console.log('JWT IN ELSE SUBJECT', jwt);
return next.handle(this.addToken(request, jwt.Token));
}));
}
}
private addToken(request: HttpRequest<any>, token: string) {
return request.clone({
headers: request.headers.set('Authorization', token)
.set('Authorization-Type', 'JWT')
.set('Content-Type', 'application/json')
.set('Cache-Control', 'no-cache')
.set('Pragma', 'no-cache')
// url: `${environment.url}/${request.url}`
});
}
// Creación del loading
async presentLoading() {
this.isLoading = true;
return await this.loadingController.create({
message: 'Caricamento in corso...',
translucent: true,
duration: 1000,
}).then(a => {
a.present().then(() => {
if (!this.isLoading) {
a.dismiss().then(() => console.log());
}
});
});
}
// Cierre del loading
async dismissLoading() {
this.isLoading = false;
return await this.loadingController.dismiss().then(() => console.log('dismissed'));
}
}
安全服务.ts
import { Injectable } from '@angular/core';
import { NativeStorage } from '@ionic-native/native-storage/ngx';
import { HttpClientModule, HttpClient } from '@angular/common/http';
import { tap, catchError } from 'rxjs/operators';
import { Tokens } from '../models/tokens';
import { Observable, throwError } from 'rxjs';
@Injectable({
providedIn: 'root'
})
export class SecurityService {
constructor(public nativeStorage: NativeStorage, private http: HttpClient) { }
public async getToken() {
return await this.nativeStorage.getItem('token');
}
refreshToken(params) {
return this.http.post<any>(`RToken/?`, params)
.pipe(tap((risultato: Tokens) => {
console.log('TOKEN FROM SERVER', risultato);
this.nativeStorage.setItem('token', risultato.token);
this.nativeStorage.setItem('refreshToken', risultato.refreshToken);
}),
catchError(err => {
console.log('ERRORE CHIAMATA RTOKEN', err);
return throwError(err);
})
);
}
}
谢谢
解决方案
推荐阅读
- angular - 渲染在 Angular 模板中异步接收的文本
- r - 将第一次出现的字符串复制到数据框的列中
- r - R - 基于 (mB) 中的 P 值将一个矩阵 (mA) 映射到另一个 (mC)
- html - two images overlapping not correctly
- java - 从可运行 jar 保存到外部文本文件
- excel - 如何编写“不等于”条件,其中数字应被解释为文本?
- android - 如何在 Flutter 中将联系人与我的应用同步?
- collections - 需要以编程方式在 shopify 集合对象(目录)中添加自定义字段,如描述
- node.js - Angular 中的 API 身份验证
- react-native - 在 React Native 中为持久会话设置访问令牌到 redux 状态是否安全?