[英]Angular 4 JWT Http Interceptor doesn't work on chain requests
I've implemented http interceptor for new HttpClient and everything works fine, token is refreshed for single request, but if I try to access the route which lazy load data from two api's I received an error and my JWT token is blacklisted. 我已经为新的HttpClient实现了HTTP拦截器,并且一切正常,令牌针对单个请求进行了刷新,但是如果我尝试访问从两个api延迟加载数据的路由,则会收到错误消息,并且我的JWT令牌已列入黑名单。
Laravel Backend Token Refresh Method: Laravel后端令牌刷新方法:
public function refreshToken() {
$token = \JWTAuth::getToken();
if (! $token) {
return response()->json(["error" => 'Token is invalid'], 401);
}
try {
$refreshedToken = \JWTAuth::refresh($token);
$user = \JWTAuth::setToken($refreshedToken)->toUser();
} catch (JWTException $e) {
return response()->json(["error" => $e->getMessage()], 400);
}
return response()->json(["token" => $refreshedToken, "user" => $user], 200);
}
Angular Http Interceptor: Angular Http拦截器:
@Injectable()
export class RefreshTokenInterceptor implements HttpInterceptor {
constructor(private injector: Injector) { }
intercept(request: HttpRequest<any>, next: HttpHandler) : Observable<HttpEvent<any>> {
return next.handle(request).catch((errorResponse: HttpErrorResponse) => {
const error = (typeof errorResponse.error !== 'object') ? JSON.parse(errorResponse.error) : errorResponse;
if(errorResponse.status === 401 && error.error === 'token_expired') {
const http = this.injector.get(HttpClient);
let token = localStorage.getItem('token');
return http.post<any>(`${environment.apiBaseUrl}token/refresh`, {},
{headers: new HttpHeaders().set('Authorization', `Bearer ${token}`)})
.flatMap(data => {
localStorage.setItem('currentUser', JSON.stringify(data));
localStorage.setItem('token', data.token);
const cloneRequest = request.clone({setHeaders: {'Authorization': `Bearer ${data.token}`}});
return next.handle(cloneRequest);
});
}
return Observable.throw(errorResponse);
});
}
}
My Route which use resolvers: 使用解析器的“我的路线”:
{
path: '',
children: [ {
path: 'create',
component: CreateCarComponent,
resolve: {
subcategories: SubCategoriesResolver,
companies: CompaniesResolver
}
}]
}
Companies Resolver: (Car resolver is simmilar to this) 公司解析器:(汽车解析器与此类似)
@Injectable()
export class CompaniesResolver implements Resolve<any> {
constructor(private _userService: UserService) {}
resolve(route: ActivatedRouteSnapshot) {
return this._userService.getCompaniesList();
}
}
User Service Method Example: 用户服务方法示例:
getUserCardsApi: string = "user/cars/all";
getCardsList() : Observable<any[]> {
return this._http.get(environment.apiBaseUrl + this.getUserCardsApi, this.jwtHeaders())
.catch(error => {
return Observable.throw(error);
});
}
Headers: 标头:
private jwtHeaders() {
let currentUser = JSON.parse(localStorage.getItem('currentUser'));
return {headers: new HttpHeaders().set('Authorization', 'Bearer ' + currentUser.token)}
}
}
Whenever I hit routes with more than 2 resolvers, first response I receive is correct and returns a refreshed token with user object and the next one after that right away returns token blacklisted. 每当我使用2个以上的解析器点击路由时,我收到的第一个响应都是正确的,并返回带有用户对象的刷新令牌,而此后的下一个立即将令牌列入黑名单。 Could you please suggest what can be the issue, I've spent too much time on solving this ( 您能否提出可能的问题,我已经花了很多时间来解决这个问题(
Update 1: 更新1:
What I noticed is that second refresh request is passing an old token rather than a new one thats why Laravel blacklisting a token 我注意到的是第二个刷新请求正在传递旧令牌,而不是新令牌,这就是Laravel将令牌列入黑名单的原因
It's some "strange" the way you inject the headers, try: 注入标头的方式有些“奇怪”,请尝试:
let httpHeaders = new HttpHeaders()
.set('Authorization', `Bearer ${data.token}`)
const cloneRequest = request.clone({ headers: httpHeaders });
return next.handle(cloneRequest );
My last attempt. 我的最后尝试。 Try check in a navigator if headers changes :( 如果标题更改,请尝试在导航器中检查:(
if(errorResponse.status === 401 && error.error === 'token_expired') {
const http = this.injector.get(HttpClient);
let token = localStorage.getItem('token');
return http.post(`${environment.apiBaseUrl}token/refresh`, {},
{headers: new HttpHeaders().set('Authorization', `Bearer ${token}`)})
.switchMap(data => {
localStorage.setItem('currentUser', JSON.stringify(data));
localStorage.setItem('token', data.token);
const cloneRequest = request.clone(
{headers: new HttpHeaders()
.set('Authorization', `Bearer ${data.token}`)
});
return next.handle(cloneRequest);
});
}
Solved and perfectly works with multiple resolvers: 已解决,可以与多个解析器完美配合使用:
export class RefreshTokenInterceptor implements HttpInterceptor {
isRefreshingToken: boolean = false;
tokenSubject: BehaviorSubject<string> = new BehaviorSubject<string>(null);
constructor(private router: Router, private injector: Injector, private _loadingBar: SlimLoadingBarService) {
}
addToken(req: HttpRequest<any>, token: string): HttpRequest<any> {
return req.clone({ setHeaders: { Authorization: `Bearer ${token}`}})
}
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpSentEvent | HttpHeaderResponse | HttpProgressEvent | HttpResponse<any> | HttpUserEvent<any>> {
return next.handle(this.addToken(req, localStorage.getItem('token')))
.catch(error => {
if (error instanceof HttpErrorResponse) {
switch ((<HttpErrorResponse>error).status) {
case 400:
return this.handle400Error(error);
case 401:
return this.handle401Error(req, next);
}
} else {
return Observable.throw(error);
}
});
}
handle401Error(req: HttpRequest<any>, next: HttpHandler) {
if (!this.isRefreshingToken) {
this.isRefreshingToken = true;
// Reset here so that the following requests wait until the token
// comes back from the refreshToken call.
this.tokenSubject.next(null);
let token = localStorage.getItem('token');
const http = this.injector.get(HttpClient);
return http.post<any>(`${environment.apiBaseUrl}token/refresh`, {},
{headers: new HttpHeaders().set('Authorization', `Bearer ${token}`)})
.switchMap((data: string) => {
if (data["token"]) {
this.tokenSubject.next(data["token"]);
return next.handle(this.addToken(req, data["token"]));
}
// If we don't get a new token, we are in trouble so logout.
return this.logoutUser();
})
.catch(error => {
// If there is an exception calling 'refreshToken', bad news so logout.
return this.logoutUser();
})
.finally(() => {
this.isRefreshingToken = false;
});
} else {
return this.tokenSubject
.filter(token => token != null)
.take(1)
.switchMap(token => {
return next.handle(this.addToken(req, token));
});
}
}
logoutUser() {
// Route to the login page (implementation up to you)
localStorage.removeItem('currentUser');
localStorage.removeItem('token');
this.router.navigate(['./auth/login']);
return Observable.throw("Error Logout");
}
handle400Error(error) {
if (error && error.status === 400 && error.error && error.error.error === 'invalid_grant') {
// If we get a 400 and the error message is 'invalid_grant', the token is no longer valid so logout.
return this.logoutUser();
}
return Observable.throw(error);
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.