我尝试使用interceptor处理来自httpClientModule的401响应。身份验证基于JWT,具有accessToken和refreshToken。如果accessToken过期,我需要请求api使用refreshToken获取一个新的。我想在生成新令牌时阻止请求,获取新令牌,然后附加新的accessToken发出请求。
这样,从拦截器发出http请求的最佳方法是什么?
我的拦截器:
@Injectable()
export class JwtService implements HttpInterceptor {
constructor(public inj: Injector){}
intercept(req : HttpRequest<any>, next : HttpHandler) : Observable<HttpEvent<any>> {
if ( this.shouldGenerateNewAccessToken(req.url) ){
const auth = this.inj.get(AuthService);
auth.getNewAccessToken().then( token => {
if (token){
const headers = { 'Authorization' : token };
const clone = req.clone( { setHeaders : headers } );
return next.handle(clone);
} else { return next.handle(req); }
}, error => {
return next.handle(req);
});
}
else {
if (APP_CONFIG['accessToken']){
const headers = { 'Authorization' : APP_CONFIG['accessToken'] };
const clone = req.clone( { setHeaders : headers });
return next.handle(clone);
} else {
return next.handle(req);
}
}
}
shouldGenerateNewAccessToken(url : string) : Boolean {
let lastupdate = APP_CONFIG['accessTokenTimestamp'];
let now = new Date().getTime();
// Let say the token expires after 5s
if ((now - lastupdate) > 5000 && APP_CONFIG['refreshToken'] && url != APP_CONFIG['apiEndPont']+'getaccesstoken'){
return true;
}
else
return false;
}
验证逻辑
getNewAccessToken() : Promise<any>{
return new Promise( (resolve, reject)=>{
this.http.post(this.api+ 'getaccesstoken', JSON.stringify({refreshToken: APP_CONFIG['refreshToken'] }), { "headers" : this.headers } ).subscribe( data => {
let res : any = data;
APP_CONFIG['accessToken'] = res.accessToken;
APP_CONFIG['accessTokenTimestamp'] = new Date().getTime();
resolve(APP_CONFIG['accessToken']);
}, err => {console.log('error'); reject(null); })
});
}
getuserinfos(){
return this.http.get(this.api+ 'getuserinfos', { "headers" : this.headers } ).subscribe( data => {
console.log('result getUserInfos =>', data);
},
( err : HttpErrorResponse ) => {
if ( err.error instanceof Error ) { console.log('An error occurred requete login:', err.error.message); }
else {
console.log('error => ', err)
}
});
}
当我调用getUserInfos()并且令牌已过期时,我收到以下错误:
错误=&gt; TypeError:您提供了&#39; undefined&#39;预期流的地方。您可以提供Observable,Promise,Array或Iterable。
这是否与此行为相关联?
更少见的是,拦截器可以选择完全处理请求本身,并组成一个新的事件流而不是调用next.handle()。这是可接受的行为,但请记住,将完全跳过进一步的拦截器。它也很少见,但拦截器在单个请求的事件流上返回多个响应是有效的。 Source
答案 0 :(得分:2)
我最终用不同的实现来管理它。这是我的拦截器:
@Injectable()
export class JwtService implements HttpInterceptor {
constructor(public inj: Injector){}
private fixUrl(url: string) {
if (url.indexOf('http://') >= 0 || url.indexOf('https://') >= 0)
return url;
else
return APP_CONFIG['apiEndPoint'] + url;
}
intercept(req : HttpRequest<any>, next : HttpHandler) : Observable<HttpEvent<any>> {
// Clone request
var headers = {}
if (APP_CONFIG['accessToken']){
headers = { 'Authorization' : APP_CONFIG['accessToken'] };
}
const cloneRequest = req.clone( { setHeaders : headers });
return next.handle(cloneRequest).do(data => {
if (data instanceof HttpResponse) {
// Some logic
}
})
.catch((res)=> {
if (res.status === 401 || res.status === 403) {
if (APP_CONFIG['accessToken'])
{
const auth = this.inj.get(AuthService);
return auth.getUpdatedAccessToken().flatMap( token => {
// Clone the previous request
let clonedRequestRepeat = req.clone({
headers: req.headers.set('Authorization' , APP_CONFIG['accessToken'] ),
url: this.fixUrl(req.url)
});
// Request agin
return next.handle(clonedRequestRepeat).do(event => {
if (event instanceof HttpResponse) {
console.log('Repeat response of server : ', event);
}
});
});
}else { return Observable.throw('Not authenticated'); }
}
else { // Not 401
return Observable.throw(res);
}
})
}
}
AuthService.ts
getUpdatedAccessToken() : Observable<any>{
return this.http.post(this.api+ 'getaccesstoken', JSON.stringify({refreshToken: APP_CONFIG['refreshToken'] }), { "headers" : this.headers } )
.map((response: any) => {
if (response.code == 0){
APP_CONFIG['accessToken'] = response.accessToken;
}
return response
})
}