当前位置:首页 > 资讯列表 >借助 SignalR 构建扫码登录机制

借助 SignalR 构建扫码登录机制

发布时间:2026-07-02 11:29:07 分类:营销学堂

SignalR 在实现扫码登录中使用

需求背景

公司开发了自己的 APP 后,需要在网页端实现扫码登录功能:网页端展示二维码 → APP 扫码确认 → 网页端自动登录。

技术选型

扫码登录的核心问题是状态同步——APP 确认登录后,网页端需要实时感知。常见方案对比:

方案 时效性 服务端压力 实现复杂度
轮询 低(依赖间隔) 高(频繁请求)
WebSocket 高(实时推送) 低(长连接)

本项目选择 SignalR(微软提供的 WebSocket 库),它封装了连接管理、自动重连、协议协商等细节,开箱即用。

服务端接入

1. 注册服务

builder.Services.AddSignalR();

如项目未包含该包,先安装 NuGet 依赖:

dotnet add package Microsoft.AspNetCore.SignalR

2. 映射 Hub 路由

app.MapHub<QRLoginHub>("/qrlogin");

注意app.UseRouting() 在 .NET 6+ 最小化 API 模式下已非必需,MapHub 可直接调用。

3. 实现 Hub 类

Hub 是 SignalR 的核心,负责接收客户端调用并向客户端推送消息。

public class QRLoginHub : Hub
{
    private readonly IConnectionManager _connectionManager;

    public QRLoginHub(IConnectionManager connectionManager)
    {
        _connectionManager = connectionManager;
    }

    public override async Task OnConnectedAsync()
    {
        // 将 ConnectionId 与业务标识绑定,供后续定向推送
        _connectionManager.Add(Context.ConnectionId);
        await base.OnConnectedAsync();
    }

    public override async Task OnDisconnectedAsync(Exception? exception)
    {
        _connectionManager.Remove(Context.ConnectionId);
        await base.OnDisconnectedAsync(exception);
    }

    public async Task GetQRCode()
    {
        // 生成包含唯一标识的二维码内容
        var qrId = Context.ConnectionId;
        var qrContent = $"https://your-site.com/scan?qrId={qrId}";
        await Clients.Caller.SendAsync("Message", new { Code = 1000, Data = qrContent });
    }
}

关键点

  • Context.ConnectionId 是每个连接的唯一标识,可作为二维码 ID 与连接的映射键。
  • 建议通过 IConnectionManager 等服务将 ConnectionId 持久化(内存/Redis),以便 API 层定向推送。
  • Clients.Caller 仅向当前调用者发送,避免广播。

客户端接入

import * as signalR from '@microsoft/signalr';

const connection = new signalR.HubConnectionBuilder()
    .withUrl('/qrlogin')
    .withAutomaticReconnect()
    .build();

connection.on('Message', (data) => {
    switch (data.code) {
        case 1000:
            // 渲染二维码
            renderQRCode(data.data);
            break;
        case 2000:
            // 登录确认,用 token 换取登录态
            loginWithToken(data.token);
            break;
    }
});

connection.start()
    .then(() => connection.invoke('GetQRCode'))
    .catch(err => console.error('连接失败:', err));

登录确认流程

完整时序如下:



APP 端扫码确认后,调用后端 API,服务端通过 IHubContext 定向推送登录结果:

[ApiController]
[Route("api/[controller]")]
public class LoginController(IHubContext<QRLoginHub> hubContext,
                              IConnectionManager connectionManager) : ControllerBase
{
    [HttpPost("confirm")]
    public async Task<IActionResult> Confirm([FromBody] ConfirmRequest request)
    {
        // 1. 校验 APP 端登录态与扫码有效性
        // 2. 根据 qrId 查找对应的 ConnectionId
        var connectionId = connectionManager.Get(request.QrId);
        if (connectionId is null)
            return NotFound("二维码已过期");

        // 3. 定向推送登录成功消息
        await hubContext.Clients.Client(connectionId)
            .SendAsync("Message", new { Code = 2000, Message = "已确认登录", Token = "..." });

        return Ok();
    }
}

前端收到 Code = 2000 后,携带 Token 请求业务接口换取正式登录态,完成登录。

补充建议

  • 二维码过期:建议设置 2~5 分钟超时,到期后通知前端刷新。
  • 安全校验:确认 API 应验证 APP 端用户身份,防止伪造确认请求。
  • 连接清理:断开连接时及时移除映射,避免内存泄漏。