.NET Core微服务CQRS场景下BaseContext如何获取登录用户填充审计字段
EF Core BaseContext 审计字段自动填充当前用户方案
原有实现问题
你当前使用的Thread.CurrentPrincipal方式不适用于ASP.NET Core微服务场景:
- ASP.NET Core默认不会给
Thread.CurrentPrincipal赋值 - 异步请求上下文切换时,线程关联的身份信息会丢失,大概率会拿到空值
推荐实现方案(基于ASP.NET Core原生认证体系)
第一步:注册HttpContext访问服务
在项目启动文件Program.cs中添加服务注册:
// 注册IHttpContextAccessor,用于访问当前请求的HttpContext builder.Services.AddHttpContextAccessor();
第二步:修改BaseContext构造函数,注入HttpContext访问器
namespace LED.OM.Core.Base { public abstract class BaseContext : DbContext { private readonly IHttpContextAccessor _httpContextAccessor; // 构造函数新增IHttpContextAccessor参数 public BaseContext(DbContextOptions options, IHttpContextAccessor httpContextAccessor) : base(options) { _httpContextAccessor = httpContextAccessor; } // SaveChanges相关重写逻辑保持不变 public override int SaveChanges() { UpdateAuditEntities(); return base.SaveChanges(); } public override int SaveChanges(bool acceptAllChangesOnSuccess) { UpdateAuditEntities(); return base.SaveChanges(acceptAllChangesOnSuccess); } public override Task<int> SaveChangesAsync(CancellationToken cancellationToken = default) { UpdateAuditEntities(); return base.SaveChangesAsync(cancellationToken); } private void UpdateAuditEntities() { // 从HttpContext的用户Claims中获取身份信息,可根据项目认证逻辑调整取的Claim类型 // 如果CreatedBy/UpdatedBy存用户名,就取ClaimTypes.Name;存用户ID就取ClaimTypes.NameIdentifier string currentUser = _httpContextAccessor.HttpContext?.User?.FindFirstValue(System.Security.Claims.ClaimTypes.NameIdentifier) ?? string.Empty; var modifiedEntries = ChangeTracker.Entries() .Where(x => x.Entity is Entity && (x.State == EntityState.Added || x.State == EntityState.Modified || x.State == EntityState.Deleted)); foreach (var entry in modifiedEntries) { var entity = (Entity)entry.Entity; var now = DateTime.UtcNow; if (entry.State == EntityState.Added) { entity.CreatedDate = now; entity.CreatedBy = currentUser; } else if (entry.State == EntityState.Deleted && entry.Entity is not IHardDelete) { entry.State = EntityState.Unchanged; entity.IsDelete = true; } else { Entry(entity).Property(x => x.CreatedBy).IsModified = false; Entry(entity).Property(x => x.CreatedDate).IsModified = false; } entity.UpdatedDate = now; entity.UpdatedBy = currentUser; } } } }
兼容无HttpContext场景(可选)
如果你的BaseContext需要在后台定时任务、MQ消费者等没有HTTP请求的场景使用,可以进一步封装ICurrentUser服务,兼容多场景:
1. 定义用户访问接口
public interface ICurrentUser { /// <summary> /// 当前登录用户ID /// </summary> string UserId { get; } /// <summary> /// 当前登录用户名 /// </summary> string UserName { get; } /// <summary> /// 是否已经认证 /// </summary> bool IsAuthenticated { get; } }
2. 实现接口
public class CurrentUser : ICurrentUser { private readonly IHttpContextAccessor _httpContextAccessor; public CurrentUser(IHttpContextAccessor httpContextAccessor) { _httpContextAccessor = httpContextAccessor; } public string UserId => _httpContextAccessor.HttpContext?.User?.FindFirstValue(System.Security.Claims.ClaimTypes.NameIdentifier) ?? string.Empty; public string UserName => _httpContextAccessor.HttpContext?.User?.FindFirstValue(System.Security.Claims.ClaimTypes.Name) ?? string.Empty; public bool IsAuthenticated => !string.IsNullOrEmpty(UserId); }
3. 注册服务
builder.Services.AddHttpContextAccessor(); builder.Services.AddScoped<ICurrentUser, CurrentUser>();
4. 注入BaseContext使用
public BaseContext(DbContextOptions options, ICurrentUser currentUser) : base(options) { _currentUser = currentUser; } // 后续直接从_currentUser.UserId取值即可
注意事项
- 确保项目已经正确配置认证中间件,
Program.cs中存在app.UseAuthentication()和app.UseAuthorization(),且顺序位于路由配置之后、端点映射之前 - 如果你的项目使用自定义Claim存储用户ID/用户名,将
FindFirstValue的参数替换为你的自定义Claim键即可
内容的提问来源于stack exchange,提问作者user16695025
相关产品推荐
相关产品推荐

