ASP .NET Core身份验证自定义ApiAuthorizationDbContext

21

我正在使用 ASP .NET Core 3.0 和 Angular 项目。我看到了这个新的 ApiAuthorizationDbContext,想要覆盖表名和用户 ID(转为 int 类型),但是我找不到任何方法来实现它。是否有人知道诀窍?

这是上下文的类,用于覆盖表名,但它创建了 AspNetUserUser 表。为什么它不像通常一样只创建一个表呢?

public class ApplicationDbContext : ApiAuthorizationDbContext<AppUser>
    {
        public ApplicationDbContext(
            DbContextOptions options,
            IOptions<OperationalStoreOptions> operationalStoreOptions) : base(options, operationalStoreOptions)
        {
        }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            base.OnModelCreating(modelBuilder);

            modelBuilder.Entity<AppUser>(entity => { entity.ToTable(name: "User"); });
            modelBuilder.Entity<AppRole>(entity => { entity.ToTable(name: "Role"); });
        }
    }

这是我的用户:

public class AppUser : IdentityUser
{
}

通常我会使用 AppUser<int> 覆盖主键,但由于 ApiAuthorizationDbContext 的原因,这种方法不起作用。

有任何想法吗?

2个回答

43

如果要使用ApiAuthorizationDbContext自定义用户和角色表,可以按照以下步骤:

  1. Create an Asp.Net Core Angular Template with Identity
  2. Add User and Role Class

    public class AppUser : IdentityUser<int>
    {
    }
    public class AppRole : IdentityRole<int>
    {
    }
    
  3. Add custom ApiAuthorizationDbContext

    /// <summary>
    /// Database abstraction for a combined <see cref="DbContext"/> using ASP.NET Identity and Identity Server.
    /// </summary>
    /// <typeparam name="TUser"></typeparam>
    /// <typeparam name="TRole"></typeparam>
    /// <typeparam name="TKey">Key of the IdentityUser entity</typeparam>
    public class KeyApiAuthorizationDbContext<TUser, TRole, TKey> : IdentityDbContext<TUser, TRole, TKey>, IPersistedGrantDbContext
        where TUser : IdentityUser<TKey>
        where TRole : IdentityRole<TKey>
        where TKey : IEquatable<TKey>
    {
        private readonly IOptions<OperationalStoreOptions> _operationalStoreOptions;
    
        /// <summary>
        /// Initializes a new instance of <see cref="ApiAuthorizationDbContext{TUser, TRole, TKey}"/>.
        /// </summary>
        /// <param name="options">The <see cref="DbContextOptions"/>.</param>
        /// <param name="operationalStoreOptions">The <see cref="IOptions{OperationalStoreOptions}"/>.</param>
        public KeyApiAuthorizationDbContext(
            DbContextOptions options,
            IOptions<OperationalStoreOptions> operationalStoreOptions)
            : base(options)
        {
            _operationalStoreOptions = operationalStoreOptions;
        }
    
        /// <summary>
        /// Gets or sets the <see cref="DbSet{PersistedGrant}"/>.
        /// </summary>
        public DbSet<PersistedGrant> PersistedGrants { get; set; }
    
        /// <summary>
        /// Gets or sets the <see cref="DbSet{DeviceFlowCodes}"/>.
        /// </summary>
        public DbSet<DeviceFlowCodes> DeviceFlowCodes { get; set; }
    
        Task<int> IPersistedGrantDbContext.SaveChangesAsync() => base.SaveChangesAsync();
    
        /// <inheritdoc />
        protected override void OnModelCreating(ModelBuilder builder)
        {
            base.OnModelCreating(builder);
            builder.ConfigurePersistedGrantContext(_operationalStoreOptions.Value);
        }
    }
    
    /// <summary>
    /// Database abstraction for a combined <see cref="DbContext"/> using ASP.NET Identity and Identity Server.
    /// </summary>
    /// <typeparam name="TUser"></typeparam>
    public class ApiAuthorizationDbContext<TUser> : KeyApiAuthorizationDbContext<TUser, IdentityRole, string>
        where TUser : IdentityUser
    {
        /// <summary>
        /// Initializes a new instance of <see cref="ApiAuthorizationDbContext{TUser}"/>.
        /// </summary>
        /// <param name="options">The <see cref="DbContextOptions"/>.</param>
        /// <param name="operationalStoreOptions">The <see cref="IOptions{OperationalStoreOptions}"/>.</param>
        public ApiAuthorizationDbContext(
            DbContextOptions options,
            IOptions<OperationalStoreOptions> operationalStoreOptions)
            : base(options, operationalStoreOptions)
        {
        }
    }
    
  4. Change DbContext

    public class ApplicationDbContext : KeyApiAuthorizationDbContext<AppUser, AppRole, int>
    {
        public ApplicationDbContext(
            DbContextOptions options,
            IOptions<OperationalStoreOptions> operationalStoreOptions) : base(options, operationalStoreOptions)
        {
        }
        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            base.OnModelCreating(modelBuilder);
    
            modelBuilder.Entity<AppUser>(entity => { entity.ToTable(name: "User"); });
            modelBuilder.Entity<AppRole>(entity => { entity.ToTable(name: "Role"); });
        }
    }
    
  5. Register user and role

    services.AddDefaultIdentity<AppUser>()
        .AddRoles<AppRole>()
        .AddEntityFrameworkStores<ApplicationDbContext>();
    
    services.AddIdentityServer()                
        .AddApiAuthorization<AppUser, ApplicationDbContext>();
    
  6. Delete existing Migrations(if database exist, you may need to delete it).

  7. Run add-migration and update-database to check the result.

目前,您需要自定义ApiAuthorizationDbContext,这个问题已经通过ApiAuthorizationDbContext force TUser to extends IdentityUser instead of IdentityUser #9548Add IdentityUser support to ApiAuthorizationDbContext #13064进行跟踪。获取最新版本可能会有一些延迟。


1
这是一个很棒的答案,完全满足了我的需求 - 特别有用,因为 MSFT 的人已经说他们不会支持内置类来完成这个功能。我有两个问题:1)由于这添加了一个带参数的构造函数,是否仍有办法编写代码,使我可以像这样做 var db = new ApplicationDbContext(); db.Add( ... ); db.SaveChangesAsync()?还是现在必须始终通过 DI 注入 DbContext?2)是否有人有一个使用此功能的托管 Blazor Wasm 项目示例,我可以查看以确保我的实现有效?谢谢! - Webreaper

0
就我所知,ApiAuthorizationDbContext只是实现了IPersistedGrantDbContext接口。看起来像是一个模板类。
为了使用自定义密钥,我已经在我的ApplicationDbContext中实现了IPersistedGrantDbContext,如下所示。
请记得在ModelCreating中调用modelBuilder.ConfigurePersistedGrantContext(_OperationalStoreOptions.Value);,如下所示。
public class ApplicationDbContext : IdentityDbContext<ApplicationUser, ApplicationRole, int,
    ApplicationUserClaim, ApplicationUserRole, ApplicationUserLogin, ApplicationRoleClaim,
    ApplicationUserToken>,
    IPersistedGrantDbContext
{
    private readonly IOptions<OperationalStoreOptions> _OperationalStoreOptions;

    public ApplicationDbContext(
        DbContextOptions options,
        IOptions<OperationalStoreOptions> operationalStoreOptions) : base(options)
    {
        this._OperationalStoreOptions = operationalStoreOptions;
    }

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        base.OnConfiguring(optionsBuilder);
        optionsBuilder.UseLazyLoadingProxies(true);

    }

    /// <summary>
    /// Gets or sets the <see cref="DbSet{PersistedGrant}"/>.
    /// </summary>
    public DbSet<PersistedGrant> PersistedGrants { get; set; }

    /// <summary>
    /// Gets or sets the <see cref="DbSet{DeviceFlowCodes}"/>.
    /// </summary>
    public DbSet<DeviceFlowCodes> DeviceFlowCodes { get; set; }

    /// <summary>
    /// Gets or sets the <see cref="DbSet{Key}"/>.
    /// </summary>
    public DbSet<Key> Keys { get; set; }

    Task<int> IPersistedGrantDbContext.SaveChangesAsync() => base.SaveChangesAsync();

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);
        modelBuilder.ConfigurePersistedGrantContext(_OperationalStoreOptions.Value);
    }
}


 public class ApplicationUser : IdentityUser<int>
{
    [Required]
    public Guid ApplicationUserRecordId { get; set; }
}

public class ApplicationRole : IdentityRole<int>
{
    public ApplicationRole()
    {

    }
    public ApplicationRole(string roleName) : base(roleName)
    {
    }
}

public class ApplicationRoleClaim : IdentityRoleClaim<int>
{

}

public class ApplicationUserClaim : IdentityUserClaim<int>
{

}

public class ApplicationUserLogin : IdentityUserLogin<int>
{

}
public class ApplicationUserRole : IdentityUserRole<int>
{

}

public class ApplicationUserToken : IdentityUserToken<int>
{

}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接