[英]Refresh token (JWT) in interceptor Angular 6
Initially, I had a function that simply checked for the presence of a token and, if it was not present, sent the user to the login header.最初,我有一个简单地检查令牌是否存在的函数,如果不存在,则将用户发送到登录标头。 Now I need to implement the logic of refreshing a token when it expires with the help of a refreshing token.现在我需要在刷新令牌的帮助下实现令牌过期时刷新的逻辑。 But I get an error 401. The refresh function does not have time to work and the work in the interceptor goes further to the error.但是我得到一个错误401。刷新功能没有时间工作,拦截器中的工作进一步导致错误。 How can I fix the code so that I can wait for the refresh to finish, get a new token and not redirect to the login page?如何修复代码,以便等待刷新完成、获取新令牌而不重定向到登录页面?
TokenInterceptor令牌拦截器
import {HttpErrorResponse, HttpEvent, HttpHandler, HttpInterceptor, HttpRequest} from "@angular/common/http";
import {Injectable, Injector} from "@angular/core";
import {AuthService} from "../services/auth.service";
import {Observable, throwError} from "rxjs";
import {catchError, tap} from "rxjs/operators";
import {Router} from "@angular/router";
import {JwtHelperService} from "@auth0/angular-jwt";
@Injectable({
providedIn: 'root'
})
export class TokenInterceptor implements HttpInterceptor{
private auth: AuthService;
constructor(private injector: Injector, private router: Router) {}
jwtHelper: JwtHelperService = new JwtHelperService();
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
this.auth = this.injector.get(AuthService);
const accToken = this.auth.getToken();
const refToken = this.auth.getRefreshToken();
if ( accToken && refToken ) {
if ( this.jwtHelper.isTokenExpired(accToken) ) {
this.auth.refreshTokens().pipe(
tap(
() => {
req = req.clone({
setHeaders: {
Authorization: `Bearer ${accToken}`
}
});
}
)
)
} else {
req = req.clone({
setHeaders: {
Authorization: `Bearer ${accToken}`
}
});
}
}
return next.handle(req).pipe(
catchError(
(error: HttpErrorResponse) => this.handleAuthError(error)
)
);
}
private handleAuthError(error: HttpErrorResponse): Observable<any>{
if (error.status === 401) {
this.router.navigate(['/login'], {
queryParams: {
sessionFailed: true
}
});
}
return throwError(error);
}
}
AuthService身份验证服务
import {Injectable} from "@angular/core";
import {HttpClient, HttpHeaders} from "@angular/common/http";
import {Observable, of} from "rxjs";
import {RefreshTokens, Tokens, User} from "../interfaces";
import {map, tap} from "rxjs/operators";
@Injectable({
providedIn: 'root'
})
export class AuthService{
private authToken = null;
private refreshToken = null;
constructor(private http: HttpClient) {}
setToken(authToken: string) {
this.authToken = authToken;
}
setRefreshToken(refreshToken: string) {
this.refreshToken = refreshToken;
}
getToken(): string {
this.authToken = localStorage.getItem('auth-token');
return this.authToken;
};
getRefreshToken(): string {
this.refreshToken = localStorage.getItem('refresh-token');
return this.refreshToken;
};
isAuthenticated(): boolean {
return !!this.authToken;
}
isRefreshToken(): boolean {
return !!this.refreshToken;
}
refreshTokens(): Observable<any> {
const httpOptions = {
headers: new HttpHeaders({
'Authorization': 'Bearer ' + this.getRefreshToken()
})
};
return this.http.post<RefreshTokens>('/api2/auth/refresh', {}, httpOptions)
.pipe(
tap((tokens: RefreshTokens) => {
localStorage.setItem('auth-token', tokens.access_token);
localStorage.setItem('refresh-token', tokens.refresh_token);
this.setToken(tokens.access_token);
this.setRefreshToken(tokens.refresh_token);
console.log('Refresh token ok');
})
);
}
}
In your example you never subscribe to your refreshTokens().pipe()
code.在您的示例中,您从不订阅您的refreshTokens().pipe()
代码。 Without a subscription, the observable won't execute.如果没有订阅,observable 将不会执行。
You have to do something like that:你必须做这样的事情:
const firstReq = cloneAndAddHeaders(req);
return next.handle(firstReq).pipe(
catchError(
err => {
if (err instanceof HttpErrorResponse) {
if (err.status === 401 || err.status === 403) {
if (firstReq.url === '/api2/auth/refresh') {
auth.setToken('');
auth.setRefreshToken('');
this.router.navigate(['/login']);
} else {
return this.auth.refreshTokens()
.pipe(mergeMap(() => next.handle(cloneAndAddHeaders(req))));
}
}
return throwError(err.message || 'Server error');
}
}
)
);
The implementation of cloneAndAddHeaders
should be something like this: cloneAndAddHeaders
的实现应该是这样的:
private cloneAndAddHeaders(request: HttpRequest<any>): HttpRequest<any> {
return request.clone({
setHeaders: {
Authorization: `YourToken`
}
});
}
req = this.auth.refreshTokens().pipe(
switchMap(() => req.clone({
setHeaders: {
Authorization: `Bearer ${this.auth.getToken()}`
}
}))
)
This will first call refreshToken
and run the tap
there, then emit request with the new this.auth.getToken()
, note that accToken
still have old value as the code is not rerun.这将首先调用refreshToken
并在那里运行tap
,然后使用新的this.auth.getToken()
发出请求,请注意accToken
仍然具有旧值,因为代码不会重新运行。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.