离子存储获取返回一个承诺“Bearer [object Promise]”如何返回一个值并将其用作授权令牌?
Posted
技术标签:
【中文标题】离子存储获取返回一个承诺“Bearer [object Promise]”如何返回一个值并将其用作授权令牌?【英文标题】:Ionic storage get returns a promise "Bearer [object Promise]" How to return a value and use it as authorization token? 【发布时间】:2019-10-13 15:49:37 【问题描述】:Ionic storage.get('token').then()
函数返回一个承诺,因此它返回一个承诺对象而不是刷新令牌。
我正在使用 JWT 进行身份验证的 Ionic 4 Angular 项目。使用 HTTP 拦截器,我能够将访问令牌作为授权标头不记名令牌发送。因为 JWT 很快就会过期,所以我需要刷新令牌。我正在使用 Python 和 Flask 后端,成功登录后,服务器响应包含访问权限,即 JWT 和刷新令牌。在我的 Python 服务器中刷新令牌,我需要使用刷新令牌作为授权标头不记名令牌向刷新端点发出 POST 请求。作为响应,服务器将访问令牌发送给我。
我遵循的步骤是:
-
成功登录后,我将访问令牌和刷新令牌保存在 Ionic 存储中。
发送访问令牌与每个
使用 Angular HTTP 拦截器请求。
如果出现错误
服务器响应带有适当的错误响应代码然后我是
发送刷新令牌请求,将刷新令牌添加为
不记名令牌授权标头
然后从服务器响应
再次将访问令牌保存在 Ionic 存储中并添加新的
每个请求的访问令牌。
我面临的问题是,当我发送刷新令牌请求而不是发送刷新令牌作为授权标头时,请求正在发送“Bearer [object Promise]”。
问题出在我的身份验证服务和getAccessTokenUsingRefreshToken( )
函数中,该函数返回一个可观察值。
因为this.storage.get(‘refresh_token’).then( )
返回一个promise,所以它返回一个promise 对象而不是token。
我的鉴权服务代码如下:
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));
));
【问题讨论】:
.then(result => return result; )
完全没有意义;它已经是该结果值的承诺。你已经从那个方法返回了一个 observable,为什么不再次使用from
?
是的,我只是想看看结果。我将编辑那部分代码
【参考方案1】:
我终于能够解决它。问题出在我的拦截器上。在我之前的代码中,我拦截了每一个请求,并用它发送了 Authorization 标头不记名令牌。这种方法的问题在于,当我尝试使用刷新令牌获取访问令牌时,我的 HTTP 拦截器也将过期的访问令牌作为授权标头发送。所以我必须在我的拦截方法中设置一个逻辑,其中刷新令牌端点中的任何请求都应该只有没有访问令牌的请求。
if (req.url.endsWith('/token/refresh'))
return next.handle(req);
所以,这是 InterceptorService.ts 的最终代码
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, map 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>>
// sending the request only for the refresh token endpoint
if (req.url.endsWith('/token/refresh'))
return next.handle(req);
let promise = this.storage.get('access_token');
return from(promise).pipe(mergeMap(token =>
const clonedReq = this.addToken(req, token);
console.log(req);
return next.handle(clonedReq).pipe(catchError(error =>
if (error instanceof HttpErrorResponse && error.status === 500)
// console.log('executed');
return this.handleAccessError(req, next);
else
return throwError(error.message);
)
) as any;
)) as any;
// 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 handleAccessError(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;
this.refreshTokenSubject.next(token);
return next.handle(this.addToken(request, token));
));
else
return this.refreshTokenSubject.pipe(
filter(token => token != null),
take(1),
switchMap(jwt =>
return next.handle(this.addToken(request, jwt));
));
这是我在 AuthenticationService 中的最终 getAccessTokenUsingRefreshToken() 方法:
getAccessTokenUsingRefreshToken(): Observable<string>
return from(this.storage.get('refresh_token')).pipe(
switchMap(refreshToken =>
const httpOptions =
headers: new HttpHeaders(
'Content-Type': 'application/json',
'Authorization': `Bearer $refreshToken`
)
;
return this.http.post<any>(`$this.url/token/refresh`, , httpOptions);
),
map(response => response.access_token),
tap(accessToken => this.storage.set(this.ACCESS_TOKEN, accessToken))
);
【讨论】:
以上是关于离子存储获取返回一个承诺“Bearer [object Promise]”如何返回一个值并将其用作授权令牌?的主要内容,如果未能解决你的问题,请参考以下文章