使用 HttpOnly cookies 时使用 SignalR 处理 JWT 刷新

问题描述 投票:0回答:1

我在 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 连接管理逻辑

谢谢你。

reactjs signalr httponly
1个回答
0
投票

我最终采用了这种方法,虽然远非最佳,但“有点效果”:

  1. 停止连接时,如果不是显式停止(例如注销),请对经过身份验证的端点执行 ping 操作以验证会话。
  2. 如果服务器响应 401,请尝试刷新令牌。
  3. 如果刷新成功,请手动重启SignalR连接。
  4. 对于显式停止,请确保设置
    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
}

'Pinger'功能:

此函数通过 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
}

虽然这个解决方案是一个起点,但我仍然对更强大的方法持开放态度。

© www.soinside.com 2019 - 2024. All rights reserved.