Blazor:如何在服务中获取IdentityUser.Id?
在服务中获取IdentityUser.Id的实现方案
可以使用AuthenticationStateProvider在服务中获取IdentityUser.Id,因为用户的GUID ID是固定不变的,即便该组件不跟踪状态变化,也不会影响获取这个固定值,完全满足你的日志范围和审计追踪需求。
核心实现步骤
- 向服务中注入
AuthenticationStateProvider - 调用其
GetAuthenticationStateAsync()方法异步获取用户认证状态 - 从用户的Claims中提取ID(通常对应
ClaimTypes.NameIdentifier类型,需与你的Identity配置一致) - 注意判断用户是否已认证,避免未登录场景下的空引用问题
针对两种用途的具体实现
1. 为日志添加用户ID范围
在服务方法中获取用户ID后,使用ILogger.BeginScope()创建日志范围,后续该范围内的所有日志都会自动带上用户ID,方便筛选:
public class BusinessService { private readonly AuthenticationStateProvider _authProvider; private readonly ILogger<BusinessService> _logger; public BusinessService(AuthenticationStateProvider authProvider, ILogger<BusinessService> logger) { _authProvider = authProvider; _logger = logger; } public async Task ProcessDataAsync() { var authState = await _authProvider.GetAuthenticationStateAsync(); var user = authState.User; if (user.Identity?.IsAuthenticated ?? false) { var userId = user.FindFirstValue(ClaimTypes.NameIdentifier); // 开启用户ID日志范围 using (_logger.BeginScope("UserId:{UserId}", userId)) { _logger.LogInformation("开始处理数据"); // 执行业务逻辑 _logger.LogInformation("数据处理完成"); } } } }
如果是Razor页面场景,也可以在页面基类中统一处理日志范围,避免每个页面重复代码。
2. EF实体审计追踪
可以通过EF的SaveChangesInterceptor统一处理实体的创建/更新人赋值,无需在每个服务方法中重复操作:
第一步:定义审计接口
public interface IAuditableEntity { string CreatedById { get; set; } DateTime CreatedAt { get; set; } string UpdatedById { get; set; } DateTime UpdatedAt { get; set; } }
第二步:实现审计拦截器
public class AuditInterceptor : SaveChangesInterceptor { private readonly AuthenticationStateProvider _authProvider; public AuditInterceptor(AuthenticationStateProvider authProvider) { _authProvider = authProvider; } public override async ValueTask<InterceptionResult<int>> SavingChangesAsync( DbContextEventData eventData, InterceptionResult<int> result, CancellationToken cancellationToken = default) { var authState = await _authProvider.GetAuthenticationStateAsync(); var userId = authState.User.FindFirstValue(ClaimTypes.NameIdentifier); if (eventData.Context != null && !string.IsNullOrEmpty(userId)) { var now = DateTime.UtcNow; // 处理新增实体 foreach (var entry in eventData.Context.ChangeTracker.Entries<IAuditableEntity>() .Where(e => e.State == EntityState.Added)) { entry.Entity.CreatedById = userId; entry.Entity.CreatedAt = now; entry.Entity.UpdatedById = userId; entry.Entity.UpdatedAt = now; } // 处理更新实体 foreach (var entry in eventData.Context.ChangeTracker.Entries<IAuditableEntity>() .Where(e => e.State == EntityState.Modified)) { entry.Entity.UpdatedById = userId; entry.Entity.UpdatedAt = now; // 避免覆盖创建时间 entry.Property(nameof(IAuditableEntity.CreatedById)).IsModified = false; entry.Property(nameof(IAuditableEntity.CreatedAt)).IsModified = false; } } return await base.SavingChangesAsync(eventData, result, cancellationToken); } }
第三步:注册拦截器到EF上下文
在Program.cs中添加:
builder.Services.AddDbContext<AppDbContext>(options => { options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")); options.AddInterceptors(new AuditInterceptor(builder.Services.BuildServiceProvider().GetRequiredService<AuthenticationStateProvider>())); });
注意事项
- 确保用户已登录:在获取用户ID前必须检查
user.Identity.IsAuthenticated,避免未登录时的空引用异常 - ClaimType一致性:确认你的Identity配置中,用户ID对应的Claim类型是
ClaimTypes.NameIdentifier,如果是自定义类型,需替换为对应的字符串 - 异步处理:
GetAuthenticationStateAsync是异步方法,服务中调用时需使用await,避免阻塞线程
内容的提问来源于stack exchange,提问作者David Thielen
相关产品推荐
相关产品推荐

