我使用以下基于类的拦截器来刷新用户令牌:
@Injectable()
export class AuthInterceptor implements HttpInterceptor {
constructor(private inject: Injector) {}
intercept(request: HttpRequest < any > , next: HttpHandler): Observable < HttpEvent < any >> {
let tokenService = this.inject.get(TokenService);
let authReq = request;
authReq = this.AddTokenHeader(request, tokenService.getAccessToken())
return next.handle(authReq).pipe(
catchError(errorData => {
if (errorData.status == 401) {
return this.handleRefreshToken(request, next);
}
return throwError(errorData);
})
);
}
AddTokenHeader(request: HttpRequest < any > , token: any) {
return request.clone({
headers: request.headers.set('Authorization', 'Bearer ' + token)
});
}
handleRefreshToken(request: HttpRequest < any > , next: HttpHandler) {
let tokenService = this.inject.get(TokenService);
let authService = this.inject.get(AuthService);
let sessionStorageService = this.inject.get(SessionStorageService);
var refreshTokenVM = new RefreshTokenVM();
refreshTokenVM.UserId = tokenService.getUserId();
refreshTokenVM.RefreshToken! = tokenService.getRefreshToken() !;
return tokenService.refreshToken(refreshTokenVM).pipe(
switchMap((data: any) => {
sessionStorageService.saveData('accessToken', data.accessToken);
sessionStorageService.saveData('refreshToken', data.refreshToken);
return next.handle(this.AddTokenHeader(request, data.accessToken))
}),
catchError(errorData => {
authService.logout();
return throwError(errorData);
})
);
}
}
在 Angular 17 中,我们必须使用函数式拦截器。我尝试将上面的代码翻译为功能拦截器:
export const authInterceptor: HttpInterceptorFn = (request: HttpRequest<unknown>, next: HttpHandlerFn): Observable<HttpEvent<unknown>> => {
const clonedRequest = request.clone({
setHeaders: {
Authorization: 'Bearer ' + 'token',
}
});
return next(clonedRequest).pipe(
catchError(errorData => {
if (errorData.status == 401) {
}
return throwError(() => errorData);
})
);
};
问题是我无法在拦截器函数中注入
tokenService
(我将令牌存储在SessionStrorage
中)。另外,我不知道如何将 handleRefreshToken
函数集成到功能拦截器中。我该如何解决这个问题?
您应该能够使用
inject函数来访问
TokenService
:
import { inject } from '@angular/core';
export const authInterceptor: HttpInterceptorFn = (request: HttpRequest<unknown>, next: HttpHandlerFn): Observable<HttpEvent<unknown>> => {
const tokenService = inject(TokenService);
const clonedRequest = request.clone({
...
关于你的第二个问题(集成刷新令牌功能),我建议你添加一个新的功能拦截器。我将吸收您原始帖子的一部分并添加一些我的观点。完整的代码将如下所示:
export const authInterceptor: HttpInterceptorFn = (request: HttpRequest<unknown>, next: HttpHandlerFn): Observable<HttpEvent<unknown>> => {
const sessionStorageService = inject(SessionStorageService);
const { accessToken } = sessionStorageService.getSession(); // will return { accessToken: 'myAccessToken', refreshToken: 'myRefreshToken'}
if (accessToken) {
const clonedRequest = request.clone({
headers: req.headers.set('Authorization', `Bearer ${accessToken}`),
});
return next(clonedRequest);
} else {
return(request);
}
};
export const unauthErrorInterceptor: HttpInterceptorFn = (req: HttpRequest<unknown>, next: HttpHandlerFn): Observable<HttpEvent<unknown>> => {
const tokenService = inject(TokenService);
const authService = inject(AuthService);
const sessionStorageService = inject(SessionStorageService);
return next(req).pipe(
catchError((error: HttpErrorResponse) => {
if (error instanceof HttpErrorResponse &&
!(req.url.includes('auth/login') || req.url.includes('auth/refresh')) && // <- this will avoid an infinite loop when the accessToken expires.
error.status === 401) {
const { refreshToken } = sessionStorageService.getSession();
if (refreshToken) {
return tokenService.refreshToken().pipe(
switchMap((refreshResult) => {
// assuming that tokenService.refreshToken() will return { accessToken: 'myNewAccessToken', refreshToken: 'myNewRefreshToken'}
sessionStorageService.saveSession(refreshResult);
return next(req.clone({
headers: req.headers.set('Authorization', `Bearer ${refreshResult.accessToken}`),
}));
}),
catchError((error) => {
console.log('error')
if (error.status == '403' || error.status === '401') {
authService.logout();
}
return throwError(() => error);
})
);
}
}
authService.logOut();
return throwError(() => new Error('Unauthorized Exception'));
})
);
};
此外,在您的
token.service.ts
中,您将需要更新标头以使用刷新令牌而不是访问令牌:
...
const AUTH_API = `${environment.apiUrl}/auth`;
const httpOptions = {
headers: new HttpHeaders({ 'Content-Type': 'application/json' })
};
...
refreshToken(): Observable<RefreshResultDto> {
const { refreshToken } = this.sessionStorageService.getSession();
return this.httpClient.post<RefreshResultDto>(`${AUTH_API}/refresh`, {}, {
...httpOptions,
headers: httpOptions.headers.set('Authorization', `Bearer ${refreshToken}`)
});
}
和
refresh-result.dto.ts
:
export interface RefreshResultDto {
accessToken: string;
refreshToken: string;
}
问候!