我在 React 前端中使用 SignalR,并通过 HttpOnly cookies 传递 JWT 令牌进行身份验证,这会阻止在 JavaScript 中访问令牌。
当服务器宕机时间过长,JWT就会过期,需要刷新。
我有一个有效的刷新令牌端点(也使用httpOnly)。
this.connection = new signalR.HubConnectionBuilder()
.withUrl(serverUrl)
.withAutomaticReconnect()
.build();
await this.connection.start();
this.connection.onclose(() => {
console.warn("SignalR Disconnected");
});
this.connection.onreconnecting((error) => {
console.warn("SignalR Reconnecting...", error);
});
this.connection.onreconnected((connectionId) => {
console.info(`SignalR Reconnected. Connection ID: ${connectionId}`);
});
JWT 仅通过 HttpOnly cookies 传递,因此
accessTokenFactory
不能按照 Microsoft 文档使用。
存在类似的讨论here,但它重点关注
accessTokenFactory
,这不适用于 HttpOnly cookie。 here公开了一个解决方案,我想知道三年后是否没有其他可以做的事情。
请避免任何“检查消息是否包含‘401’或‘未经授权’”。
在使用 HttpOnly cookie 时,是否有标准或推荐的方法来处理 SignalR 的 JWT 刷新,无需完全重新实现 SignalR 连接管理逻辑?
谢谢你。
我最终采用了这种方法,虽然远非最佳,但“有点效果”:
stopConnectionCalled
布尔值,以避免在名义断开连接期间进行不必要的令牌刷新尝试。// The `stopConnectionCalled` field prevents refresh attempts during intentional disconnections (e.g., logout).
private stopConnectionCalled: boolean = false;
async initConnection() {
// Reset `stopConnectionCalled` whenever initializing the connection.
this.stopConnectionCalled = false;
this.connection = new signalR.HubConnectionBuilder()
.withUrl(serverUrl)
.withAutomaticReconnect()
.build();
await this.connection.start();
this.connection.onclose(async () => {
// Attempt to recover the connection only if it wasn't manually stopped.
if (!this.stopConnectionCalled) {
const refreshedSuccessfully = await this.tryIfRefreshTokenNeeded(id);
if (refreshedSuccessfully) {
// Restart the SignalR connection manually after token refresh.
await this.connection?.start();
}
}
});
}
async stopConnection() {
// Mark as explicitly stopped to prevent unnecessary recovery attempts.
this.stopConnectionCalled = true;
// ...nominal stop connection logic
}
此函数通过 ping 经过身份验证的端点来检查是否需要刷新令牌,并在需要时尝试刷新令牌:
private async tryIfRefreshTokenNeeded(id: string): Promise<boolean> {
try {
// Ping the server with an authenticated request.
await api.server.pingAuthenticatedNoAutoRefresh();
} catch (error) {
// Handle 401 Unauthorized responses by refreshing the token.
if (error instanceof Response && error.status === 401) {
// 401 Unauthorized detected on ping, attempting token refresh
try {
await api.account.refreshToken();
return true; // Refresh succeeded
} catch {
return false; // failed to refresh the token
}
}
return false; // Any other error
}
return true; // No refresh needed
}
虽然这个解决方案是一个起点,但我仍然对更强大的方法持开放态度。