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

.NET6 Blazor WASM IdentityServer4登录事件获取user始终为null

问题原因
  • 监听的事件类型完全错误:ClientAuthenticationSuccess 是客户端认证成功事件,触发时机是Blazor WASM这类OAuth客户端使用客户端ID、密钥向IdentityServer请求令牌的阶段,此时认证流程还未完成用户身份绑定,HttpContext.User 中不存在有效的用户身份声明,UserManager.GetUserAsync 自然无法匹配到用户实体。
  • 取值方式存在设计缺陷:IdentityServer的事件对象本身携带了认证全流程的上下文信息,不需要依赖IHttpContextAccessor间接获取用户数据,这种跨组件获取HttpContext的方式在IdentityServer事件管道中本身就存在上下文不同步的问题,稳定性极差。
  • 事件注册方式有误:直接通过services.AddTransient<IEventSink, CustomEventSink>()注册自定义事件Sink,会覆盖IdentityServer内置的默认事件处理实现,同时可能导致事件生命周期异常。
正确实现方案

1. 修正事件监听逻辑

首先移除对ClientAuthenticationSuccess事件的监听,用户主动登录成功/失败对应的事件ID分别是EventIds.UserLoginSuccess、EventIds.UserLoginFailure,同时直接从事件对象中提取用户ID查询用户,不再依赖HttpContext。修正后的CustomEventSink代码如下:

public class CustomEventSink : IEventSink
{
    private readonly ApplicationDbContext _context;
    private readonly UserManager<ApplicationUser> _userManager;

    // 移除不必要的IHttpContextAccessor依赖
    public CustomEventSink(ApplicationDbContext context, UserManager<ApplicationUser> userManager)
    {
        _context = context;
        _userManager = userManager;
    }

    public async Task PersistAsync(Event evt)
    {
        // 处理用户登录成功事件
        if (evt.Id == EventIds.UserLoginSuccess)
        {
            var loginEvent = evt as UserLoginSuccessEvent;
            if (loginEvent == null) return;

            try
            {
                // 直接从事件携带的用户主键(SubjectId)查询用户,无需走HttpContext
                var user = await _userManager.FindByIdAsync(loginEvent.SubjectId);
                if (user == null) return;

                var log = new EventLog
                {
                    EventName = "Login",
                    EventDescription = $"{evt.Name} ({evt.Id}), 登录客户端:{loginEvent.ClientId}, 登录IP:{loginEvent.RemoteIpAddress}",
                    Created = DateTime.Now,
                    UserName = user.UserName
                };
                _context.EventLog.Add(log);
                await _context.SaveChangesAsync();
            }
            catch (Exception ex)
            {
                // 建议在此处添加异常日志,不要空吞异常
            }
        }

        // 处理用户登录失败事件
        if (evt.Id == EventIds.UserLoginFailure)
        {
            var failEvent = evt as UserLoginFailureEvent;
            if (failEvent == null) return;

            try
            {
                var log = new EventLog
                {
                    EventName = "Login Failure",
                    EventDescription = $"{evt.Name} ({evt.Id}), 尝试登录用户名:{failEvent.Username}, 失败原因:{failEvent.Error}, 客户端:{failEvent.ClientId}, IP:{failEvent.RemoteIpAddress}",
                    Created = DateTime.Now,
                    UserName = failEvent.Username
                };
                _context.EventLog.Add(log);
                await _context.SaveChangesAsync();
            }
            catch (Exception ex)
            {
                // 建议在此处添加异常日志,不要空吞异常
            }
        }
    }
}

2. 修正IdentityServer服务配置

首先打开信息类事件的开关(用户登录事件属于Information级别,不打开开关不会触发),然后使用IdentityServer内置的AddEventSink方法注册自定义Sink,删除原来单独注册IEventSink的代码,避免覆盖默认实现:

services.AddIdentityServer(options =>
    {
        options.Events.RaiseSuccessEvents = true;
        options.Events.RaiseFailureEvents = true;
        options.Events.RaiseErrorEvents = true;
        // 必须打开,否则UserLogin相关事件不会触发
        options.Events.RaiseInformationEvents = true;
    })
    .AddApiAuthorization<ApplicationUser, ApplicationDbContext>(options =>
    {
        options.IdentityResources["openid"].UserClaims.Add("role");
        options.ApiResources.Single().UserClaims.Add("role");
    })
    // 链式注册自定义事件Sink,不要单独在外面注册IEventSink
    .AddEventSink<CustomEventSink>();

// 删除下面这行旧的注册代码
// services.AddTransient<IEventSink, CustomEventSink>();

补充说明

  • 如果需要记录令牌签发、刷新令牌的行为,可以额外监听EventIds.TokenIssuedSuccess事件,但注意该事件在每次access_token刷新时都会触发,如果只需要统计用户主动输入账号密码的登录行为,仅监听UserLoginSuccess即可,不会产生重复日志。
  • 原有中间件管道顺序(UseIdentityServer放在UseAuthentication之前)无需调整,符合IdentityServer的中间件注册要求。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 15:24:27