离子storage.get('token').then()
函数返回一个承诺,因此它返回一个承诺对象而不是刷新令牌。
我正在使用 JWT 进行身份验证的 Ionic 4 Angular 项目。使用 HTTP 拦截器,我能够将访问令牌作为授权标头不记名令牌发送。因为 JWT 很快就会过期,所以我需要刷新令牌。我正在使用 Python 和 Flask 后端,成功登录后,服务器响应包含访问,即 JWT 和刷新令牌。在我的 Python 服务器中刷新令牌,我需要使用刷新令牌作为授权标头不记名令牌向刷新端点发出 POST 请求。作为响应,服务器向我发送访问令牌。
我遵循的步骤是:
- 成功登录后,我将访问令牌和刷新令牌保存在 Ionic 存储中。
- 使用 Angular HTTP 拦截器随每个请求发送访问令牌。
- 如果服务器响应出现错误并带有适当的错误响应代码,那么我正在发送刷新令牌请求,将刷新令牌添加为不记名令牌授权标头
- 然后从服务器响应中再次将访问令牌保存在 Ionic 存储中,并在每个请求中添加新的访问令牌。
我面临的问题是当我发送刷新令牌请求而不是发送刷新令牌作为授权标头时,请求正在发送“Bearer [object Promise]”。
问题出在我的身份验证服务和getAccessTokenUsingRefreshToken( )
返回可观察值的函数中。asthis.storage.get(‘refresh_token’).then( )
返回一个承诺,所以它返回一个承诺对象而不是令牌。
我的身份验证服务的代码如下:
import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders, HttpErrorResponse, HttpRequest } from '@angular/common/http';
import { BehaviorSubject, throwError, Observable, from } from 'rxjs';
import { Platform, AlertController } from '@ionic/angular';
import { Storage } from '@ionic/storage';
import { JwtHelperService } from '@auth0/angular-jwt';
import { tap, catchError, mergeMap } from 'rxjs/operators';
import { User } from '../models/user.model';
@Injectable({
providedIn: 'root'
})
export class AuthenticationService {
constructor(
private http: HttpClient,
private helper: JwtHelperService,
private storage: Storage,
private platform: Platform,
private alertController: AlertController) {
// this.platform.ready().then(() => {
// this.checkToken();
// });
}
url = 'http://localhost:5000';
ACCESS_TOKEN = 'access_token';
REFRESH_TOKEN = 'refresh_token';
user = null;
token;
// refreshToken;
authenticationState = new BehaviorSubject(false);
register(user: User): Observable<User> {
// if (user.id === null)
console.log(user);
return this.http.post<User>(`${this.url}/register`, user)
.pipe(
tap(res => {
this.storage.set(this.ACCESS_TOKEN, res[this.ACCESS_TOKEN]);
this.storage.set(this.REFRESH_TOKEN, res[this.REFRESH_TOKEN]);
this.user = this.helper.decodeToken(res[this.ACCESS_TOKEN]);
// console.log(this.storage.get(this.REFRESH_TOKEN));
this.authenticationState.next(true);
}),
);
}
login(data) {
return this.http.post(`${this.url}/auth`, data)
.pipe(
tap(res => {
this.storage.set(this.ACCESS_TOKEN, res[this.ACCESS_TOKEN]);
this.storage.set(this.REFRESH_TOKEN, res[this.REFRESH_TOKEN]);
this.user = this.helper.decodeToken(res[this.ACCESS_TOKEN]);
// this.storage.get(this.REFRESH_TOKEN);
// console.log(this.storage.get(this.ACCESS_TOKEN));
// console.log(this.getRefreshToken());
this.authenticationState.next(true);
}),
);
}
logout() {
this.storage.remove(this.ACCESS_TOKEN).then(() => {
this.authenticationState.next(false);
});
this.storage.remove(this.REFRESH_TOKEN);
}
private addToken(token: any) {
if (token) {
const httpOptions = {
headers: new HttpHeaders({
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`
})
};
return httpOptions;
}
}
getAccessTokenUsingRefreshToken() {
const refreshToken = this.storage.get('refresh_token').then((result) => {
return result;
});
const httpOptions = {
headers: new HttpHeaders({
'Content-Type': 'application/json',
'Authorization': `Bearer ${refreshToken}`
})
};
return this.http.post<any>(`${this.url}/token/refresh`, 'body', httpOptions ).pipe(tap(tokens => {
console.log(tokens['access_token']);
console.log(tokens);
this.storage.set(this.ACCESS_TOKEN, tokens[this.ACCESS_TOKEN]);
console.log(this.storage.get('access_token'));
}));
}
checkToken(): Promise<any> {
return this.storage.get(this.ACCESS_TOKEN).then(token => {
if (token) {
this.token = token;
if (!this.helper.isTokenExpired(this.token)) {
this.user = this.helper.decodeToken(this.token);
this.authenticationState.next(true);
} else {
this.storage.remove(this.ACCESS_TOKEN);
this.storage.remove(this.REFRESH_TOKEN);
}
}
});
}
getToken() {
return this.storage.get(this.ACCESS_TOKEN);
}
isAuthenticated() {
return this.authenticationState.value;
}
}
这是我的 HTTP 拦截器代码
import { Injectable } from '@angular/core';
import { HttpInterceptor, HttpRequest, HttpHandler, HttpEvent, HttpHeaders, HttpErrorResponse } from '@angular/common/http';
import { Observable, from, throwError, BehaviorSubject } from 'rxjs';
import { Storage } from '@ionic/storage';
// import { _throw } from 'rxjs/observable/throw';
import { catchError, mergeMap, switchMap, filter, take } from 'rxjs/operators';
import { AlertController } from '@ionic/angular';
import { AuthenticationService } from './authentication.service';
@Injectable({
providedIn: 'root'
})
export class InterceptorService implements HttpInterceptor {
private isRefreshing = false;
private refreshTokenSubject: BehaviorSubject<any> = new BehaviorSubject<any>(null);
constructor(private storage: Storage, private alertCtrl: AlertController, private authenticationService: AuthenticationService) { }
intercept(req: HttpRequest<any>, next: HttpHandler):
Observable<HttpEvent<any>> {
let promise = this.storage.get('access_token');
return from(promise).pipe(mergeMap(token => {
const clonedReq = this.addToken(req, token);
return next.handle(clonedReq).pipe(catchError(error => {
if (error instanceof HttpErrorResponse && error.status === 401) {
// console.log('executed');
console.log(req);
return this.handle401Error(req, next);
} else {
return throwError(error.message);
}
})
);
}
));
}
// Adds the token to your headers if it exists
private addToken(request: HttpRequest<any>, token: any) {
if (token) {
let clone: HttpRequest<any>;
clone = request.clone({
setHeaders: {
Accept: `application/json`,
'Content-Type': `application/json`,
Authorization: `Bearer ${token}`
}
});
return clone;
}
return request;
}
private handle401Error(request: HttpRequest<any>, next: HttpHandler) {
if (!this.isRefreshing) {
this.isRefreshing = true;
this.refreshTokenSubject.next(null);
return this.authenticationService.getAccessTokenUsingRefreshToken().pipe(
switchMap((token: any) => {
this.isRefreshing = false;
console.log(token);
console.log('executed');
this.refreshTokenSubject.next(token.access_token);
return next.handle(this.addToken(request, token.access_token));
}));
} else {
return this.refreshTokenSubject.pipe(
filter(token => token != null),
take(1),
switchMap(access_token => {
return next.handle(this.addToken(request, access_token));
}));
}
}
}