问题
I'm using two JWT tokens - Refresh Token(expires after 7 days) and Access Token (expires after 15 min). They are stored on httpOnly cookies and can be accessed via server. Refresh methods signs new token and store it on a cookie. I need to check if these tokens are expired after every request like this:
@Injectable()
export class AuthInterceptor implements HttpInterceptor {
constructor(private authService: AuthService, private cookieService: CookieService) { }
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
const expirationToken = this.cookieService.get('tokenexp'); // access token expiration
const expirationTokenRefresh = this.cookieService.get('tokenrefexp'); // refresh expiration
// refresh token -> access token -> original request
return of(Number(expirationTokenRefresh) < Date.now()).pipe(
mergeMap(expire => expire
? this.authService.refreshTokenRefresh()
: of(Number(expirationToken) < Date.now())
),
mergeMap(expire => expire
? this.authService.refreshToken()
: of(true)
),
mergeMap(ok => next.handle(req.clone({ withCredentials: true })))
);
}
}
// auth service
refreshToken() {
return this.http.get(`${BACKEND_URL}/refreshtoken`);
}
refreshTokenRefresh() {
return this.http.get(`${BACKEND_URL}/refreshtokenref`);
}
I may send one request to refresh a token, and then another request to refresh second token, and finally the original request with updated cookies. In summary, I may need to send requests before my original request.
Problem is: There is a loop of requests going to AuthInterceptor
every time a request is made. Request one and two (tokens) shouldn't call AuthInterceptor
.
回答1:
Do a conditional check to skip the interceptor if the request url is for token.
@Injectable()
export class AuthInterceptor implements HttpInterceptor {
constructor(private authService: AuthService, private cookieService: CookieService) { }
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
if(req.url===`${BACKEND_URL}/refreshtoken` || req.url ===`${BACKEND_URL}/refreshtokenref`)
return next.handle(req.clone({ withCredentials: true }))
const expirationToken = this.cookieService.get('tokenexp'); // access token expiration
const expirationTokenRefresh = this.cookieService.get('tokenrefexp'); // refresh expiration
// refresh token -> access token -> original request
return of(Number(expirationTokenRefresh) < Date.now()).pipe(
mergeMap(expire => expire
? this.authService.refreshTokenRefresh()
: of(Number(expirationToken) < Date.now())
),
mergeMap(expire => expire
? this.authService.refreshToken()
: of(true)
),
mergeMap(ok => next.handle(req.clone({ withCredentials: true })))
);
}
}
// auth service
refreshToken() {
return this.http.get(`${BACKEND_URL}/refreshtoken`);
}
refreshTokenRefresh() {
return this.http.get(`${BACKEND_URL}/refreshtokenref`);
}
Do agree to @Xinan that interceptor can sometimes be more of a problem. Create your own http service might be better
class HttpService{
constructoer(private _http:HttpClient)
preIntercept(url,options){
this._http.get(tokenUrl).pipe(
map(res=>{
//do your stuff
return {url,options}
}))
}
get(url,options={}){
return this.preIntercept(url,options).pipe(
mergeMap(({url,options})=>this._http.get(url,options))
}
}
来源:https://stackoverflow.com/questions/52781145/how-to-avoid-multiple-httpinterceptor-calls-while-navigating-between-rxjs-mergem