问题
I have created a service for calling API from my angular application. In that service, I have declared ROOT_URL and TOKEN variables and assigned values for these.
Below the declaration, there are few get methods to API using the above ROOT_URL and TOKEN.
Issue i am facing is, this TOKEN value is expired every 24 hours so that i have to change the value everyday. I use the previous TOKEN to get a refresh token using postman.
Can some one give me a solution how can i implement this will happen automatically every time when TOKEN expires?
回答1:
You can make use of the HTTP interceptor. You can check this article from Angular Academy.
Below you can find an example which I have implemented according to my needs (I have used that article as a starting point for this implementation). This example assumes that you're generating a refresh token on your back-end. On my back-end, I'm generating an access token (with a short living duration) and a refresh token (with a higher living duration). I'm using the refresh tokens only for generating new access tokens and not for authorization. You can store the tokens for example on local storage or cookie and retrieve them from there in a service.
import { Injectable } from '@angular/core';
import { HttpRequest, HttpHandler, HttpEvent, HttpInterceptor, HttpErrorResponse } from '@angular/common/http';
import { Observable, throwError } from 'rxjs';
import { switchMap, catchError } from 'rxjs/operators';
import { AuthService } from './auth.service';
import { IUserResponse } from '../shared/user.model';
@Injectable()
export class AuthInterceptor implements HttpInterceptor {
// for avoiding entering an infinite loop
private isRefreshing = false;
constructor(private authService: AuthService) {}
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
if (this.authService.accessToken) {
request = this.setToken(request, this.authService.accessToken);
}
return next.handle(request).pipe(
catchError(error => {
if (error instanceof HttpErrorResponse && error.status === 401 && this.authService.refreshToken) {
return this.handleAuthorizationError(request, next);
} else {
return throwError(error);
}
})
);
}
private setToken(request: HttpRequest<any>, token: string): HttpRequest<any> {
return request.clone({ setHeaders: { Authorization: `Bearer ${token}` } });
}
private handleAuthorizationError(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
if (!this.isRefreshing) {
this.isRefreshing = true;
// I have created a route on my back-end to generate a new access token
return this.authService.getRefreshToken().pipe(
switchMap((response: IUserResponse) => {
this.isRefreshing = false;
return next.handle(this.setToken(request, response.user.accessToken));
})
);
} else {
return next.handle(request);
}
}
}
回答2:
Usually, the HTTP response header that comes from the API has something that indicates that this client once was authenticated but now has an expired token. Typically, the response header has a property called token-expired or www-authenticate; you have to check this before starting the refreshes token process.
Code sample:
AuthInterceptor
import { Injectable } from '@angular/core';
import {
HttpInterceptor,
HttpRequest,
HttpHandler,
HttpEvent,
HttpErrorResponse
} from '@angular/common/http';
import { AuthService } from '../services/auth.service';
import { Observable, BehaviorSubject, throwError } from 'rxjs';
import { environment } from 'src/environments/environment';
import { filter, switchMap, take, catchError } from 'rxjs/operators';
@Injectable()
export class AuthInterceptor implements HttpInterceptor {
private tryingRefreshing = false;
private refreshTokenSubject: BehaviorSubject<any> = new BehaviorSubject<any>(null);
constructor(public authService: AuthService) { }
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
const token = this.authService.getToken();
request = this.addAuthorization(request, token);
return next.handle(request).pipe(catchError(error => {
if (error instanceof HttpErrorResponse && error.status === 401) {
const tokenExpired = error.headers.get('token-expired');
if (tokenExpired) {
return this.handle401Error(request, next);
}
this.authService.logout();
return throwError(error);
} else {
return throwError(error);
}
}));
}
private handle401Error(request: HttpRequest<any>, next: HttpHandler) {
if (!this.tryingRefreshing) {
this.tryingRefreshing = true;
this.refreshTokenSubject.next(null);
return this.authService.refreshToken().pipe(
switchMap((token: any) => {
this.tryingRefreshing = false;
this.refreshTokenSubject.next(token);
return next.handle(this.addAuthorization(request, token));
}));
} else {
return this.refreshTokenSubject.pipe(
filter(token => token != null),
take(1),
switchMap(jwt => {
return next.handle(this.addAuthorization(request, jwt));
}));
}
}
addAuthorization(httpRequest: HttpRequest<any>, token: string) {
return httpRequest = httpRequest.clone({
setHeaders: {
Authorization: `Bearer ${token}`
}
});
}
}
Refresh token
This is just a sample method to show the share() approach.
refreshToken(): Observable<string> {
return this.http.post<any>(`${this.baseUrl}/auth/token/refresh-token`, {}, { withCredentials: true })
.pipe(
share(),
map((authResponse) => {
this.currentAuthSubject.next(authResponse);
this.addToLocalStorage(authResponse);
return authResponse.token;
}));
}
来源:https://stackoverflow.com/questions/60744304/refresh-token-angular