将 IdentityServer4 与自定义配置 DBContext 一起使用

Posted

技术标签:

【中文标题】将 IdentityServer4 与自定义配置 DBContext 一起使用【英文标题】:using IdentityServer4 with custom Configration DBContext 【发布时间】:2017-11-20 22:36:46 【问题描述】:

我创建了一个自定义的IConfigurationDbContext,以便将 IDS4 与 Oracle 一起使用。

  public class IdentityConfigurationDbContext :  DbContext, IConfigurationDbContext 
        private readonly ConfigurationStoreOptions storeOptions;

        public IdentityConfigurationDbContext(DbContextOptions<IdentityServerDbContext> options)
         : base(options) 
    

    public IdentityConfigurationDbContext(DbContextOptions<ConfigurationDbContext> options, ConfigurationStoreOptions storeOptions)
        : base(options) 
        this.storeOptions = storeOptions ?? throw new ArgumentNullException(nameof(storeOptions));
    

    public DbSet<Client> Clients  get; set; 
    public DbSet<IdentityResource> IdentityResources  get; set; 
    public DbSet<ApiResource> ApiResources  get; set; 

    protected override void OnModelCreating(ModelBuilder modelBuilder) 
        modelBuilder.ConfigureClientContext(storeOptions);
        modelBuilder.ConfigureResourcesContext(storeOptions);

        base.OnModelCreating(modelBuilder);
    
  

在配置服务中:

 services.AddIdentityServer()
                .AddTemporarySigningCredential()
                .AddAspNetIdentity<ApplicationUser>();

我也有我的自定义IClientStore,它像这样添加到容器中:

services.AddScoped<IClientStore, ClientStore>();

当我运行 IdentityConfigurationDbContext 迁移时,我收到此错误:

System.InvalidOperationException: No database provider has been configured for this DbContext.

我试过这样做:

services.AddDbContext<IdentityConfigurationDbContext>(builder => builder.UseOracle(connectionString, options => 
                options.MigrationsAssembly(migrationsAssembly);
                options.MigrationsHistoryTable("EF_MIGRATION_HISTORY");
            ));

这是在 IDS4 中使用自定义 dbcontext 的正确方法吗?以及如何解决此问题并完成我的迁移工作?

【问题讨论】:

【参考方案1】:

您无需创建自定义ConfigurationDbContext 或事件IDbContextFactory 即可切换到使用不同的数据库。使用IdentityServer4.EntityFramework 2.3.2 版,您可以:

namespace DL.STS.Host

    public class Startup
    
        ...

        public void ConfigureServices(IServiceCollection services)
        
            string connectionString = _configuration.GetConnectionString("appDbConnection");

            string migrationsAssembly = typeof(Startup).GetTypeInfo().Assembly
                .GetName().Name;

            services
               .AddIdentityServer()
               .AddConfigurationStore(options =>
               
                   options.ConfigureDbContext = builder =>
                       // I made up this extension method "UseOracle",
                       // but this is where you plug your database in
                       builder.UseOracle(connectionString,
                           sql => sql.MigrationsAssembly(migrationsAssembly));
               )
               ...;

            ...
        

        ...
    

将配置/操作存储分离到自己的项目/程序集中?

如果您想很好地布置您的解决方案并希望将配置存储和操作存储(以及身份用户存储)分离到它们自己的类库/程序集中怎么办?

根据文档,您可以使用 -o 指定输出迁移文件夹目标:

dotnet ef migrations add InitialIdentityServerPersistedGrantDbMigration -c PersistedGrantDbContext -o Data/Migrations/IdentityServer/PersistedGrantDb
dotnet ef migrations add InitialIdentityServerConfigurationDbMigration -c ConfigurationDbContext -o Data/Migrations/IdentityServer/ConfigurationDb

但谁喜欢在迁移时记住/输入这么长的路径?那么你可能会想:继承自 IdentityServer 的自定义 ConfigurationDbContext 和一个单独的项目怎么样:

using IdentityServer4.EntityFramework.DbContexts;
using IdentityServer4.EntityFramework.Options;
using Microsoft.EntityFrameworkCore;

namespace DL.STS.Data.ConfigurationStore.EFCore

    public class AppConfigurationDbContext : ConfigurationDbContext
    
        public AppConfigurationDbContext(DbContextOptions<ConfigurationDbContext> options, 
            ConfigurationStoreOptions storeOptions) : base(options, storeOptions)
        
        
    

常见错误

我认为这是人们遇到麻烦的地方。当您执行Add-Migration 时,您会遇到:

无法创建AppConfigurationDbContext 类型的对象。有关设计时支持的不同模式,请参阅https://go.microsoft.com/fwlink/?linkid=851728。

尝试激活 DL.STS.Data.ConfigurationStore.EFCore.AppConfigurationDbContext 时无法解析类型 Microsoft.EntityFrameworkCore.DbContextOptions&lt;IdentityServer4.EntityFramework.DbContexts.ConfigurationDbContext&gt; 的服务。

我不认为,目前,有办法解决它。

还有其他方法吗?

事实证明这实际上很容易。似乎您不能从 IdentityServer 继承自己的 DbContext。所以摆脱它,并在那个单独的库/程序集中创建一个扩展方法:

using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using System.Reflection;

namespace DL.STS.Data.ConfigurationStore.EFCore.Extensions

    public static class IdentityServerBuilderExtensions
    
        public static IIdentityServerBuilder AddEFConfigurationStore(
            this IIdentityServerBuilder builder, string connectionString)
        
            string assemblyNamespace = typeof(IdentityServerBuilderExtensions)
                .GetTypeInfo()
                .Assembly
                .GetName()
                .Name;

            builder.AddConfigurationStore(options =>
                options.ConfigureDbContext = b =>
                    b.UseSqlServer(connectionString, optionsBuilder =>
                        optionsBuilder.MigrationsAssembly(assemblyNamespace)
                    )
            );

            return builder;
        
    

然后在您的网络项目上Startup.cs

public void ConfigureServices(IServiceCollection services)

    ...

    string connectionString = _configuration.GetConnectionString("appDbConnection");

    services
        .AddIdentityServer()
        .AddDeveloperSigningCredential()
        .AddEFConfigurationStore(connectionString)
        ...;

    ...

当你使用PM&gt; Add-Migration AddConfigurationTables -Context ConfigurationDbContext 时,默认项目是那个单独的库/程序集:

【讨论】:

我得到“找不到名为 'ConfigurationDbContext' 的 DbContext”。请检查我的问题***.com/questions/61263843/… 这个答案太棒了,正是我所需要的。我有一个 n 层项目,其中上下文和迁移与 Web 项目分开(完全如此处所述)。在遵循这个答案之后,唯一要添加的是我运行以生成和应用迁移的确切脚本。从 API 项目目录:dotnet ef migrations add AddConfigurationTables -s ../&lt;Api Project Folder&gt; --context ConfigurationDbContext -o Migrations\IdentityServer4dotnet ef database update AddConfigurationTables -s ../&lt;Api Project Folder&gt; --context ConfigurationDbContext【参考方案2】:

在最近的版本中,Identityserver 框架确实支持配置存储、操作存储的自定义实现。这也适用于迁移

例如见下文

            public class CustomPersistsDbContext : DbContext, IPersistedGrantDbContext
                
                

在 OnModelCreating(ModelBuilder modelBuilder) 中,我必须手动添加关系:

                protected override void OnModelCreating(ModelBuilder modelBuilder)
                
                    //Optional: The version of .NET Core, used by Ef Core Migration history table
                    modelBuilder.HasAnnotation("ProductVersion", "2.2.0-rtm-35687");

          //.. Your custom code

    //PersistentDbContext
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.DeviceFlowCodes", b =>
                    
                        b.Property<string>("UserCode")
                            .ValueGeneratedOnAdd()
                            .HasMaxLength(200);

                        b.Property<string>("ClientId")
                            .IsRequired()
                            .HasMaxLength(200);

                        b.Property<DateTime>("CreationTime");

                        b.Property<string>("Data")
                            .IsRequired()
                            .HasMaxLength(50000);

                        b.Property<string>("DeviceCode")
                            .IsRequired()
                            .HasMaxLength(200);

                        b.Property<DateTime?>("Expiration")
                            .IsRequired();

                        b.Property<string>("SubjectId")
                            .HasMaxLength(200);

                        b.HasKey("UserCode");

                        b.HasIndex("DeviceCode")
                            .IsUnique();

                        b.HasIndex("UserCode")
                            .IsUnique();

                        b.ToTable("DeviceCodes");
                    );

                    modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.PersistedGrant", b =>
                    
                        b.Property<string>("Key")
                            .HasMaxLength(200);

                        b.Property<string>("ClientId")
                            .IsRequired()
                            .HasMaxLength(200);

                        b.Property<DateTime>("CreationTime");

                        b.Property<string>("Data")
                            .IsRequired()
                            .HasMaxLength(50000);

                        b.Property<DateTime?>("Expiration");

                        b.Property<string>("SubjectId")
                            .HasMaxLength(200);

                        b.Property<string>("Type")
                            .IsRequired()
                            .HasMaxLength(50);

                        b.HasKey("Key");

                        b.HasIndex("SubjectId", "ClientId", "Type");

                        b.ToTable("PersistedGrants");
                    );
                

在服务启动时

 .AddOperationalStore<CustomPersistsDbContext>(options =>

【讨论】:

【参考方案3】:

我尝试了不同的方法。而不是实现IConfigurationDbContext 我继承自IdentityServer4.EntityFramework.DbContexts.ConfigurationDbContext

public class CustomConfigurationDbContext : ConfigurationDbContext

    public CustomConfigurationDbContext(DbContextOptions<ConfigurationDbContext> options,
        ConfigurationStoreOptions storeOptions)
        : base(options, storeOptions)
    
    

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    
        if (!optionsBuilder.IsConfigured)
        
            //...

            base.OnConfiguring(optionsBuilder);
        
    

在startup.cs中

services.AddIdentityServer()
                .AddTemporarySigningCredential()
                .AddConfigurationStore(
                    builder => builder.UseSqlServer(connectionString, options => options.MigrationsAssembly(migrationsAssembly)))
                .AddOperationalStore(
                    builder => builder.UseSqlServer(connectionString, options => options.MigrationsAssembly(migrationsAssembly)))
                .AddAspNetIdentity<ApplicationUser>();

它就像一个魅力。 免责声明:这不是我的想法。我只是不记得它的来源。

【讨论】:

我这样做了,但在迁移过程中出现此错误:在“IdentityConfigurationDbContext”上找不到无参数构造函数。将无参数构造函数添加到“IdentityConfigurationDbContext”或在与“IdentityConfigurationDbContext”相同的程序集中添加“IDbContextFactory”的实现。 即使你继承自 ConfigurationDbContext 是的,我还添加了一个 No parameterless constructor 同样的东西。 找不到前任。方法。 hm 这不会使用 CustomConfigurationDbContext 除非 AddConfigurationStore 但随后会出现“无法解析类型 'Microsoft.EntityFrameworkCore .DbContextOptions`1[IdentityServer4.EntityFramework.DbContexts.ConfigurationDbContext] 的服务”错误尝试激活时..”【参考方案4】:

添加IDbContextFactory 解决了这个问题。

public class IdentityConfigurationDbContextFactory : IDbContextFactory<IdentityConfigurationDbContext> 

        public IdentityConfigurationDbContext Create(DbContextFactoryOptions options) 
            var optionsBuilder = new DbContextOptionsBuilder<ConfigurationDbContext>();
            var config = new ConfigurationBuilder()
                             .SetBasePath(options.ContentRootPath)
                             .AddJsonFile("appsettings.json")
                              .AddJsonFile($"appsettings.options.EnvironmentName.json", true)

                             .Build();

            optionsBuilder.UseOracle(config.GetConnectionString("DefaultConnection"));

            return new IdentityConfigurationDbContext(optionsBuilder.Options, new ConfigurationStoreOptions());
        
    

【讨论】:

哦,是的,你应该有一个IDbContextFactory&lt;CustomConfigurationDbContext&gt;的实现 您是如何在 Startup.cs 中注册 CustomConfigurationDbContext 的?我收到此错误 System.InvalidOperationException: 'No service for type CustomConfigurationDbContext' has been registered。' 检查我的答案 above 这是我在启动时添加的唯一内容【参考方案5】:

我认为最简单的方法是使用 ConfigurationDbContext 的参数 T,如下所示。它适用于 net core 3.0

public class ConfigurationDataContext : ConfigurationDbContext<ConfigurationDataContext>

    public ConfigurationDataContext(DbContextOptions<ConfigurationDataContext> options, ConfigurationStoreOptions storeOptions)
    : base(options, storeOptions)
    
    

    protected override void OnModelCreating(ModelBuilder builder)
    
        base.OnModelCreating(builder);

        builder.ApplyConfigurationsFromAssembly(typeof(MyConfigurationsAssemby).Assembly);
    

【讨论】:

【参考方案6】:

我使用了您的解决方案,稍作改动。我附在代码下面。 在您的测试方法 OnModelCreating 中,您声明了两个方法

...
modelBuilder.ConfigureClientContext(configurationStoreOptions);
modelBuilder.ConfigureResourcesContext(configurationStoreOptions);

modelBuilder.ConfigurePersistedGrantContext(operationalStoreOptions); // need to add

指的是PersistedGrants和DeviceFlowCodes,这很好,但你需要添加

ConfigurePersistedGrantContext also.

here too there is info?

public class MYCustomDbContext : DbContext, IPersistedGrantDbContext, IConfigurationDbContext


public DbSet<PersistedGrant> PersistedGrants  get; set; 

public DbSet<DeviceFlowCodes> DeviceFlowCodes  get; set; 

........

protected override void OnModelCreating(ModelBuilder modelBuilder)

    base.OnModelCreating(modelBuilder);

    if (modelBuilder is null)
       throw new ArgumentNullException(nameof(modelBuilder));

    ConfigurationStoreOptions storeConfigurationOptions = new ConfigurationStoreOptions();
    OperationalStoreOptions storeOperationalOptions = new OperationalStoreOptions();

    modelBuilder.ConfigureClientContext(configurationStoreOptions);
    modelBuilder.ConfigureResourcesContext(configurationStoreOptions);
    modelBuilder.ConfigurePersistedGrantContext(operationalStoreOptions);


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

public Task<int> SaveChangesAsync() => base.SaveChangesAsync();
public void ConfigureServices(IServiceCollection services)

......
 services.AddOperationalStore<MYCustomDbContext>(options => 
                    // this enables automatic token cleanup.
                    options.EnableTokenCleanup = true;
                    options.TokenCleanupInterval = 3600; 
.....

【讨论】:

【参考方案7】:

我用 IdentityServer4.EntityFramework (4.1.2) 的最新版本制作了这个:

我创建了一个名为ApplicationConfigurationDbContext 的类继承自ConfigurationDbContext&lt;TContext&gt;,其中TContext 是我的类

public class ApplicationConfigurationDbContext : ConfigurationDbContext<ApplicationConfigurationDbContext>

    public ApplicationConfigurationDbContext(DbContextOptions<ApplicationConfigurationDbContext> options, ConfigurationStoreOptions storeOptions) : base(options, storeOptions)
    

    

    // My own entities...

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

        // My own configurations...
    

然后在Startup 类中我注册了我的 DbContext,最后在调用 AddIdentityServer() 扩展方法之后,我也链接了 AddConfigurationStore() 扩展方法,就是这样!。

public class Startup

    // Hide for brevity

    public void ConfigureServices(IServiceCollection services)
    
        var builder = services.AddIdentityServer(options =>
        
            // My options...
        )
        .AddConfigurationStore<ApplicationConfigurationDbContext>(options =>
        
            options.ConfigureDbContext = b => b.UseSqlServer(connectionString,
                sql => sql.MigrationsAssembly(migrationsAssembly));
        )
        .AddAspNetIdentity<ApplicationUser>();
    

当我希望添加迁移时,我会这样做:

dotnet ef migrations add "Add_New_Table" --context "ApplicationConfigurationDbContext" --startup-project "My.Start.Project" --project "Target.Project" --output-dir "Migrations/ConfigurationDb"

通过 .NET CLI here 获得有关 Entity Framework Core 工具的更多信息。

【讨论】:

以上是关于将 IdentityServer4 与自定义配置 DBContext 一起使用的主要内容,如果未能解决你的问题,请参考以下文章

Angular 材质 Snackbar 配置与自定义 panelClass 配置,用于错误、成功、警告消息

Excel自定义颜色美化单元格颜色配置技巧与自定义颜色主题

Bamboo 构建计划与自定义环境配置的部署计划

java注解与自定义注解

java注解与自定义注解

FastDFS客户端与自定义文件存储系统