利用 SignalR 实现扫码登录功能
本文包含AI辅助创作内容
需求背景
公司开发了自己的 APP 后,需要在网页端实现扫码登录功能:网页端展示二维码 → APP 扫码确认 → 网页端自动登录。
技术选型
扫码登录的核心问题是状态同步——APP 确认登录后,网页端需要实时感知。常见方案对比:
本项目选择 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 端用户身份,防止伪造确认请求。
- 连接清理:断开连接时及时移除映射,避免内存泄漏。
请先 登录后发表评论 ~