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

基于Azure AD认证的.NET Core应用用户关联记录追踪方案咨询

针对Azure AD认证.NET Core应用的操作追踪解决方案

核心结论

你需要创建极简本地用户表(仅存Azure AD用户核心标识和基础信息),登录时同步用户信息,业务表直接关联用户的Azure AD ObjectId即可,不需要额外元表。

具体实现步骤

  • 创建本地用户表
    只保留必要字段,避免冗余:

    public class AppUser
    {
        // Azure AD用户唯一标识,作为主键
        public string ObjectId { get; set; } = string.Empty;
        // 用户显示名称
        public string DisplayName { get; set; } = string.Empty;
        // 用户邮箱
        public string Email { get; set; } = string.Empty;
        // 最后同步Azure AD信息的时间
        public DateTime LastSyncTime { get; set; }
    }
    

    在EF Core DbContext中配置:

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<AppUser>()
            .HasKey(u => u.ObjectId);
    }
    
  • 登录时同步Azure AD用户信息
    在Azure AD OpenID Connect配置中,利用OnTokenValidated事件同步用户数据:

    services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
        .AddMicrosoftIdentityWebApp(options =>
        {
            // 你的Azure AD配置
            Configuration.Bind("AzureAd", options);
    
            options.Events = new OpenIdConnectEvents
            {
                OnTokenValidated = async context =>
                {
                    // 从Claims中获取Azure AD用户核心信息
                    var objectId = context.Principal.FindFirstValue(ClaimTypes.NameIdentifier);
                    var displayName = context.Principal.FindFirstValue(ClaimTypes.Name);
                    var email = context.Principal.FindFirstValue(ClaimTypes.Email);
    
                    if (string.IsNullOrEmpty(objectId)) return;
    
                    // 获取DbContext实例
                    var dbContext = context.HttpContext.RequestServices.GetRequiredService<AppDbContext>();
                    var existingUser = await dbContext.AppUsers.FindAsync(objectId);
    
                    if (existingUser == null)
                    {
                        // 新增用户
                        dbContext.AppUsers.Add(new AppUser
                        {
                            ObjectId = objectId,
                            DisplayName = displayName ?? string.Empty,
                            Email = email ?? string.Empty,
                            LastSyncTime = DateTime.UtcNow
                        });
                    }
                    else
                    {
                        // 同步更新变更的信息(比如名称、邮箱)
                        bool hasChanged = false;
                        if (existingUser.DisplayName != displayName)
                        {
                            existingUser.DisplayName = displayName ?? string.Empty;
                            hasChanged = true;
                        }
                        if (existingUser.Email != email)
                        {
                            existingUser.Email = email ?? string.Empty;
                            hasChanged = true;
                        }
                        if (hasChanged)
                        {
                            existingUser.LastSyncTime = DateTime.UtcNow;
                        }
                    }
    
                    await dbContext.SaveChangesAsync();
                }
            };
        });
    
  • 业务表添加追踪字段
    给需要追踪的业务实体添加以下字段:

    public class BusinessEntity
    {
        // 业务主键
        public int Id { get; set; }
        // 其他业务字段...
    
        // 追踪字段
        public DateTime CreatedAt { get; set; }
        public DateTime UpdatedAt { get; set; }
        public string CreatedByObjectId { get; set; } = string.Empty;
        public string UpdatedByObjectId { get; set; } = string.Empty;
    
        // 关联本地用户表(可选,用于查询时联表获取用户信息)
        public AppUser CreatedBy { get; set; } = null!;
        public AppUser UpdatedBy { get; set; } = null!;
    }
    

    在DbContext中配置外键:

    modelBuilder.Entity<BusinessEntity>()
        .HasOne(e => e.CreatedBy)
        .WithMany()
        .HasForeignKey(e => e.CreatedByObjectId)
        .OnDelete(DeleteBehavior.Restrict);
    
    modelBuilder.Entity<BusinessEntity>()
        .HasOne(e => e.UpdatedBy)
        .WithMany()
        .HasForeignKey(e => e.UpdatedByObjectId)
        .OnDelete(DeleteBehavior.Restrict);
    
  • 自动填充追踪字段
    重载EF Core的SaveChangesAsync方法,自动设置创建/更新时间和操作用户,避免重复代码:

    public class AppDbContext : DbContext
    {
        private readonly IHttpContextAccessor _httpContextAccessor;
    
        public AppDbContext(DbContextOptions<AppDbContext> options, IHttpContextAccessor httpContextAccessor)
            : base(options)
        {
            _httpContextAccessor = httpContextAccessor;
        }
    
        public override async Task<int> SaveChangesAsync(CancellationToken cancellationToken = default)
        {
            var currentUserId = _httpContextAccessor.HttpContext?.User.FindFirstValue(ClaimTypes.NameIdentifier);
            var now = DateTime.UtcNow;
    
            foreach (var entry in ChangeTracker.Entries())
            {
                if (entry.Entity is BusinessEntity entity)
                {
                    switch (entry.State)
                    {
                        case EntityState.Added:
                            entity.CreatedAt = now;
                            entity.UpdatedAt = now;
                            entity.CreatedByObjectId = currentUserId ?? string.Empty;
                            entity.UpdatedByObjectId = currentUserId ?? string.Empty;
                            break;
                        case EntityState.Modified:
                            entity.UpdatedAt = now;
                            entity.UpdatedByObjectId = currentUserId ?? string.Empty;
                            // 禁止修改创建时间和创建人
                            entry.Property(nameof(entity.CreatedAt)).IsModified = false;
                            entry.Property(nameof(entity.CreatedByObjectId)).IsModified = false;
                            break;
                    }
                }
            }
    
            return await base.SaveChangesAsync(cancellationToken);
        }
    }
    

为什么不用IdentityUser?

纯Azure AD认证场景下,ASP.NET Core Identity的AspNetUserLogins表是用来关联本地用户账户和第三方登录的,但你不需要本地账户体系,Identity整套框架会带来不必要的冗余(比如AspNetUsers、AspNetRoles等表)。自己建极简用户表更轻量,完全适配你的需求。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.16 16:41:32