-
-
Save carlosocarvalho/facd15a64f6e613986a15b2673338a8b to your computer and use it in GitHub Desktop.
Interceptador HTTP em Angular 5 para adicionar o Token JWT nos request, e renovar o token caso expirado funcionando com múltiplas chamadas
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { Injectable, Injector } from '@angular/core'; | |
import { | |
HttpInterceptor, | |
HttpRequest, | |
HttpHandler, | |
HttpEvent | |
} from '@angular/common/http'; | |
import { Observable } from 'rxjs/Observable'; | |
import { BehaviorSubject } from 'rxjs/BehaviorSubject'; | |
import { finalize } from 'rxjs/operators/finalize'; | |
import { catchError } from 'rxjs/operators/catchError'; | |
import { switchMap } from 'rxjs/operators/switchMap'; | |
import { take } from 'rxjs/operators'; | |
import { filter } from 'rxjs/operators'; | |
import 'rxjs/add/observable/throw'; | |
import { AuthService } from '@app/core/security/auth/auth.service'; | |
/** | |
* Interceptador HTTP para adicionar o Token nas requisições ao serve | |
* e mapear os erros a fim de verificar erro de token expirado | |
* tentando renova-lo caso possivel, aceitando multiplas chamadas assincronas | |
* com o token expirado criando uma espece de "fila" até que a 1º chamada que | |
* solicitou o token seja resolvida. | |
* | |
* @author Filipe Mansano | |
* @version 1.0 | |
* @since 2018-01-02 | |
*/ | |
@Injectable() | |
export class JWTInterceptor implements HttpInterceptor { | |
isRefreshingToken: boolean = false; | |
tokenSubject: BehaviorSubject<string> = new BehaviorSubject<string>(null); | |
constructor(private inj: Injector) { } | |
/** | |
* Função que clona o request adicionando o token nos cabeçalhos | |
* @param request | |
* @param token | |
*/ | |
addToken(request: HttpRequest<any>, token: string): HttpRequest<any> { | |
return request.clone({ | |
setHeaders: { | |
Authorization: `Bearer ${token}` | |
} | |
}); | |
} | |
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> { | |
const authService = this.inj.get(AuthService); | |
/** | |
* no login nao quero interceptar nada, então | |
* do continuidade no fluxo do request | |
*/ | |
if (request.url.includes("/auth/login")) { | |
return next.handle(request); | |
} | |
const requestJWT = this.addToken(request, authService.getToken().token); | |
// capturando os possiveis erros do request | |
return next.handle(requestJWT).pipe( | |
catchError(err => { | |
/** | |
* Caso o erro seja um código 401 | |
* inicio a logica pra o refresh do token | |
*/ | |
if (err.status === 401) { | |
// verifico se o motivo foi token expirado | |
if (err.error && err.error.errorMessage.includes("expirado")) { | |
// se o refresh de token não tiver em andamento, inicio ele | |
if (!this.isRefreshingToken) { | |
this.isRefreshingToken = true; | |
/** | |
* Reiniciando o valor do token aqui para que os proximos pedidos | |
* aguardem até que o token volte da chamada de atualização do token. | |
*/ | |
this.tokenSubject.next(null); | |
return authService.refreshToken().pipe( | |
finalize(() => { | |
this.isRefreshingToken = false; | |
}), | |
switchMap((newToken: string) => { | |
if(newToken){ | |
this.tokenSubject.next(newToken); | |
return next.handle(this.addToken(request, newToken)); | |
} | |
/** | |
* se não for retornado o token, deu algum problema | |
* então desconecto o usuario e lanço a exceção pra frente | |
*/ | |
authService.logoff(); | |
return Observable.throw("Não foi possivel obter o token"); | |
}), | |
/** | |
* Caso de qualquer erro no refresh do token | |
* não tem oque fazer, então deslogo o usuario | |
*/ | |
catchError(errTokenRefresh => { | |
authService.logoff(); | |
return Observable.throw(errTokenRefresh); | |
}) | |
); | |
} | |
/** | |
* Caso o token esteja em processe de atualização | |
*/ | |
else { | |
return this.tokenSubject.pipe( | |
filter(token => token != null), | |
take(1), | |
switchMap(token => { | |
return next.handle(this.addToken(request,token)); | |
}) | |
); | |
} | |
} | |
/** | |
* Caso o motivo do erro 401 não contenha a palavra expirado | |
* deslogo o usuario, pois é algum erro desconhecido... | |
*/ | |
else { | |
authService.logoff(); | |
return Observable.throw(err); | |
} | |
} | |
// qualquer outro erro lanço uma exceção | |
else{ | |
return Observable.throw(err); | |
} | |
}) | |
); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment