You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何追踪ASP.NET Core Identity缓存登录的用户连接事件?

捕获Blazor Server+ASP.NET Core Identity的用户会话认证事件

针对你的场景(Blazor Server架构、ASP.NET Core Identity Cookie认证),要追踪用户每次连接的会话记录(含时间、IP),可以通过以下几种方式结合实现,覆盖主动登录和Cookie自动登录的场景:

1. 利用Cookie认证事件拦截自动登录/主动登录

ASP.NET Core Identity的Cookie认证提供了事件钩子,可在Cookie验证或用户登录时触发记录逻辑:

配置CookieAuthenticationEvents

在Program.cs中配置Identity的Cookie选项,添加OnValidatePrincipal(处理Cookie自动验证)和OnSigningIn(处理主动登录)事件:

builder.Services.ConfigureApplicationCookie(options =>
{
    options.Events = new CookieAuthenticationEvents
    {
        // 每次Cookie验证通过时触发(包括用户通过Cookie自动登录)
        OnValidatePrincipal = async context =>
        {
            if (context.Principal?.Identity?.IsAuthenticated == true)
            {
                var userId = context.Principal.FindFirstValue(ClaimTypes.NameIdentifier);
                var remoteIp = context.HttpContext.Connection.RemoteIpAddress?.ToString();
                var currentTime = DateTime.UtcNow;

                // 获取自定义的会话跟踪服务
                var trackingService = context.HttpContext.RequestServices
                    .GetRequiredService<IUserTrackingService>();

                // 可选:避免短时间内重复记录(比如间隔30分钟以上才记录新会话)
                var lastTrackedTime = await trackingService.GetLastTrackedTimeAsync(userId);
                if (lastTrackedTime == null || currentTime - lastTrackedTime > TimeSpan.FromMinutes(30))
                {
                    await trackingService.TrackUserSessionAsync(userId, currentTime, remoteIp);
                }
            }
        },
        // 用户主动登录时触发(比如输入账号密码登录)
        OnSigningIn = async context =>
        {
            var userId = context.Principal.FindFirstValue(ClaimTypes.NameIdentifier);
            var remoteIp = context.HttpContext.Connection.RemoteIpAddress?.ToString();
            var currentTime = DateTime.UtcNow;

            var trackingService = context.HttpContext.RequestServices
                .GetRequiredService<IUserTrackingService>();
            await trackingService.TrackUserSessionAsync(userId, currentTime, remoteIp);
        }
    };
});

2. 监听Blazor Server的SignalR连接事件

Blazor Server基于SignalR通信,用户每次打开应用、刷新页面都会建立新的SignalR连接,可在Hub中重写OnConnectedAsync方法记录会话:

自定义Blazor Hub

创建自定义Hub并重写连接事件:

using Microsoft.AspNetCore.SignalR;
using System.Security.Claims;

public class AppHub : Hub
{
    private readonly IUserTrackingService _trackingService;
    private readonly IHttpContextAccessor _httpContextAccessor;

    public AppHub(IUserTrackingService trackingService, IHttpContextAccessor httpContextAccessor)
    {
        _trackingService = trackingService;
        _httpContextAccessor = httpContextAccessor;
    }

    public override async Task OnConnectedAsync()
    {
        var user = Context.User;
        if (user?.Identity?.IsAuthenticated == true)
        {
            var userId = user.FindFirstValue(ClaimTypes.NameIdentifier);
            // 获取远程IP:优先从初始HTTP请求的HttpContext获取, fallback到SignalR连接的IP
            var remoteIp = _httpContextAccessor.HttpContext?.Connection.RemoteIpAddress?.ToString()
                           ?? Context.Connection.RemoteIpAddress?.ToString();
            var currentTime = DateTime.UtcNow;

            // 同样添加重复记录校验
            var lastTrackedTime = await trackingService.GetLastTrackedTimeAsync(userId);
            if (lastTrackedTime == null || currentTime - lastTrackedTime > TimeSpan.FromMinutes(30))
            {
                await _trackingService.TrackUserSessionAsync(userId, currentTime, remoteIp);
            }
        }

        await base.OnConnectedAsync();
    }
}

注册必要服务

在Program.cs中注册IHttpContextAccessor(用于Hub中获取初始HTTP请求的IP)和自定义服务:

// 注册HttpContext访问器
builder.Services.AddHttpContextAccessor();
// 注册会话跟踪服务
builder.Services.AddScoped<IUserTrackingService, UserTrackingService>();
// 注册自定义Hub(如果Blazor Server未自动注册默认Hub)
builder.Services.AddSignalR().AddHubOptions<AppHub>(options =>
{
    // 可选配置Hub选项
});

3. 实现会话跟踪服务

定义用于存储会话记录的实体和服务:

会话日志实体

using Microsoft.AspNetCore.Identity;

public class UserSessionLog
{
    public int Id { get; set; }
    public string UserId { get; set; } = string.Empty;
    public DateTime SessionStartTime { get; set; }
    public string? RemoteIpAddress { get; set; }

    // 关联Identity用户
    public IdentityUser? User { get; set; }
}

跟踪服务接口与实现

using Microsoft.EntityFrameworkCore;

public interface IUserTrackingService
{
    Task TrackUserSessionAsync(string userId, DateTime timestamp, string? remoteIp);
    Task<DateTime?> GetLastTrackedTimeAsync(string userId);
}

public class UserTrackingService : IUserTrackingService
{
    private readonly ApplicationDbContext _dbContext;

    public UserTrackingService(ApplicationDbContext dbContext)
    {
        _dbContext = dbContext;
    }

    public async Task TrackUserSessionAsync(string userId, DateTime timestamp, string? remoteIp)
    {
        var log = new UserSessionLog
        {
            UserId = userId,
            SessionStartTime = timestamp,
            RemoteIpAddress = remoteIp
        };

        _dbContext.UserSessionLogs.Add(log);
        await _dbContext.SaveChangesAsync();
    }

    public async Task<DateTime?> GetLastTrackedTimeAsync(string userId)
    {
        return await _dbContext.UserSessionLogs
            .Where(l => l.UserId == userId)
            .OrderByDescending(l => l.SessionStartTime)
            .Select(l => l.SessionStartTime)
            .FirstOrDefaultAsync();
    }
}

更新DbContext

在你的ApplicationDbContext中添加DbSet:

public DbSet<UserSessionLog> UserSessionLogs { get; set; }

然后执行迁移创建对应的数据库表。

关键说明

  • 重复记录避免:通过检查用户上次记录的时间间隔,避免短时间内(比如用户频繁刷新页面)产生大量重复记录,可根据业务需求调整时间阈值。
  • IP获取:Blazor Server中SignalR连接的IP可能需要从初始HTTP请求的HttpContext获取,因为SignalR升级后的连接IP可能不准确,所以结合IHttpContextAccessor和Context.Connection.RemoteIpAddress两种方式。
  • 场景覆盖:Cookie事件覆盖主动登录和Cookie自动验证场景,Hub连接事件覆盖用户重新打开应用、刷新页面的场景,两者结合可以完整追踪用户的所有会话连接。

内容的提问来源于stack exchange,提问作者David Thielen

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 09:00:43