我有一个具有以下功能的 Angular 应用程序:
访问令牌的
login
方法。getRefreshToken
方法,用于检索 刷新令牌。JwtInterceptor
:
tokenInvalid
错误,触发另一个拦截器调用getRefreshToken
方法。问题:
当API未运行时,应用程序不断尝试循环调用
getRefreshToken
方法来获取新的刷新令牌,我不明白为什么。
身份验证基础设施:
private auth$: Observable<AuthenticationToken> | null | undefined;
login(email: string, password: string): Observable<AuthenticationToken> {
if (!this.auth$) {
const body = new HttpParams()
.set('username', email)
.set('password', password);
const headers = new HttpHeaders({
'Content-Type': 'application/x-www-form-urlencoded',
});
this.auth$ = this.httpClient
.post<AuthenticationToken>(
'http://localhost:8080/realms/master/protocol/openid-connect/token/',
body.toString(),
{ headers }
)
.pipe(shareReplay(1));
}
return this.auth$;
}
refreshToken() {
return from(this.storage.get('refresh_token')).pipe(
switchMap((storedRefreshToken) => {
const body = new HttpParams()
.set('grant_type', 'refresh_token')
.set('refresh_token', storedRefreshToken);
const headers = new HttpHeaders().set(
'Content-Type',
'application/x-www-form-urlencoded'
);
return this.httpClient
.post<AuthenticationToken>(
'http://localhost:8080/master/clovis/protocol/openid-connect/token/',
body,
{ headers }
)
.pipe(shareReplay(1));
})
);
}
Jwt拦截器:
export function jwtInterceptor(request: HttpRequest<any>, next: HttpHandlerFn) {
const storage = inject(Storage);
return from(storage.get('access_token')).pipe(
switchMap((token) => {
const newRequest = request.clone({
setHeaders: {
Authorization: 'Bearer ' + token,
},
});
return next(newRequest);
})
);
}
export const refreshInterceptor: HttpInterceptorFn = (req, next) => {
const authInfra = inject(AuthenticationInfrastructure);
const localStorage = inject(LocalStorageAuthenticationInfrastructure);
const authApp = inject(AuthenticationApplication);
return next(req).pipe(
catchError((error: HttpErrorResponse) => {
if (error.status === 401 || error.status === 0) {
return authInfra.refreshToken().pipe(
switchMap((tokenResponse: WithToken) => {
localStorage.startSession({
access_token: tokenResponse.access_token,
refresh_token: tokenResponse.refresh_token,
});
const newAuthReq = req.clone({
headers: req.headers.set(
'Authorization',
`Bearer ${tokenResponse.access_token}`
),
});
return next(newAuthReq);
}),
catchError((refreshError) => {
console.error('Error refreshing token', refreshError);
authApp.logout();
return throwError(() => new Error('Token refresh failed!'));
})
);
} else {
return throwError(() => error);
}
})
);
};
我的浏览器控制台:
user.application.ts:12
POST http://localhost:8080/master/clovis/protocol/openid-connect/token/ net::ERR_CONNECTION_REFUSED
user.application.ts:12
POST http://localhost:8080/master/clovis/protocol/openid-connect/token/ net::ERR_CONNECTION_REFUSED
user.application.ts:12
POST http://localhost:8080/master/clovis/protocol/openid-connect/token/ net::ERR_CONNECTION_REFUSED
user.application.ts:12
POST http://localhost:8080/master/clovis/protocol/openid-connect/token/ net::ERR_CONNECTION_REFUSED
user.application.ts:12
POST http://localhost:8080/master/clovis/protocol/openid-connect/token/ net::ERR_CONNECTION_REFUSED
user.application.ts:12
POST http://localhost:8080/master/clovis/protocol/openid-connect/token/ net::ERR_CONNECTION_REFUSED
观察代码,主要问题似乎出在
refereshToken()
逻辑上。
根据我所发生的情况,
refreshInterceptor
捕获 401 或 0 错误并尝试刷新令牌。由于 API 已损坏,令牌刷新请求也会失败,从而再次触发拦截器,这可能是无限循环的原因。
我们需要通过处理
refreshToken()
调用失败来停止循环。
尝试在代码中添加以下 catch 片段:
export const refreshInterceptor: HttpInterceptorFn = (req, next) => {
const authInfra = inject(AuthenticationInfrastructure);
const localStorage = inject(LocalStorageAuthenticationInfrastructure);
const authApp = inject(AuthenticationApplication);
return next(req).pipe(
catchError((error: HttpErrorResponse) => {
if (error.status === 401 || error.status === 0) {
return authInfra.refreshToken().pipe(
switchMap((tokenResponse: WithToken) => {
localStorage.startSession({
access_token: tokenResponse.access_token,
refresh_token: tokenResponse.refresh_token,
});
const newAuthReq = req.clone({
headers: req.headers.set(
'Authorization',
`Bearer ${tokenResponse.access_token}`
),
});
return next(newAuthReq);
}),
catchError((refreshError) => {
console.error('Error refreshing token', refreshError);
authApp.logout(); // End the session
return throwError(() => new Error('Token refresh failed!')); // Prevent further retries
})
);
} else {
return throwError(() => error);
}
})
);
};
希望这有帮助!