identityserver旨在实现可扩展性,其中一个可扩展点是用于identityserver所需数据的存储机制。本快速入门展示了如何配置identityserver以使用entityframework core(ef)作为此数据的存储机制(而不是使用我们迄今为止使用的内存中实现)。

注意
除了手动配置ef支持外,还有一个identityserver模板可用于创建具有ef支持的新项目。使用创建它。有关更多信息,请参见此处dotnet new is4ef

15.1 identityserver4.entityframework

我们要移动到数据库有两种类型的数据:

  • 配置数据
  • 运营数据

配置数据是有关资源和客户端的配置信息。操作数据是identityserver在使用时生成的信息,例如令牌,代码和同意。这些存储使用接口建模,我们在identityserver4.entityframework.storage nuget包中提供这些接口的ef实现。

注册我们的ef实现的扩展方法包含在identityserver4.entityframework nuget包中(引用identityserver4.entityframework.storage)。立即从identityserver项目添加对identityserver4.entityframework nuget包的引用:

cd quickstart/src/identityserver
dotnet add package identityserver4.entityframework

15.2 使用的sqlserver

鉴于ef的灵活性,您可以使用任何ef支持的数据库。对于本快速入门,我们将使用visual studio附带的sqld的localdb版本。

15.3 数据库架构更改和使用ef迁移

identityserver4.entityframework.storage包中包含从identityserver的模型映射实体类。作为identityserver模型变化,所以会在实体类identityserver4.entityframework.storage。当您使用identityserver4.entityframework.storage并随着时间的推移升级时,您将负责自己的数据库架构以及实体类更改时该架构所需的更改。管理这些更改的一种方法是使用ef迁移,此快速入门将显示如何完成此操作。如果迁移不是您的首选项,那么您可以以任何您认为合适的方式管理架构更改。

注意
identityserver4.entityframework.storage中的实体维护sqlserver的最新sql脚本。他们就在这里。

15.4 配置存储

接下来的步骤是,以取代当前addinmemoryclientsaddinmemoryidentityresourcesaddinmemoryapiresources在在startup.cs方法configureservices。我们将使用以下代码替换它们:

const string connectionstring = @"data source=(localdb)\mssqllocaldb;database=identityserver4.quickstart.entityframework-2.0.0;trusted_connection=yes;";
var migrationsassembly = typeof(startup).gettypeinfo().assembly.getname().name;

// configure identity server with in-memory stores, keys, clients and scopes
services.addidentityserver()
    .addtestusers(config.getusers())
    // this adds the config data from db (clients, resources)
    .addconfigurationstore(options =>
    {
        options.configuredbcontext = b =>
            b.usesqlserver(connectionstring,
                sql => sql.migrationsassembly(migrationsassembly));
    })
    // this adds the operational data from db (codes, tokens, consents)
    .addoperationalstore(options =>
    {
        options.configuredbcontext = b =>
            b.usesqlserver(connectionstring,
                sql => sql.migrationsassembly(migrationsassembly));

        // this enables automatic token cleanup. this is optional.
        options.enabletokencleanup = true;
    });

您可能需要将这些命名空间添加到文件中:

using microsoft.entityframeworkcore;
using system.reflection;

上面的代码是对连接字符串进行硬编码,如果您愿意,您可以随意更改。

addconfigurationstoreaddoperationalstore注册ef支持的存储实现。

在添加存储的调用内部,configuredbcontext属性的赋值注册委托以配置数据库提供程序dbcontextoptionsbuilder。在这种情况下,我们调用usesqlserver注册sqlserver。您也可以看出,这是使用连接字符串的位置。

最后,假设将使用ef迁移(至少对于此快速入门),调用将migrationsassembly用于通知ef将包含迁移代码的主机项目(这是必需的,因为它与包含dbcontext类的程序集不同)。

我们接下来会添加迁移。

15.5 添加迁移

要创建迁移,请在identityserver项目目录中打开命令提示符。在命令提示符下运行以下两个命令:

dotnet ef migrations add initialidentityserverpersistedgrantdbmigration -c persistedgrantdbcontext -o data/migrations/identityserver/persistedgrantdb
dotnet ef migrations add initialidentityserverconfigurationdbmigration -c configurationdbcontext -o data/migrations/identityserver/configurationdb

您现在应该在项目中看到~/data/migrations/identityserver文件夹。其中包含新创建的迁移的代码。

15.6 初始化数据库

现在我们已经进行了迁移,我们可以编写代码来从迁移中创建数据库。我们还将使用我们在之前的快速入门中定义的内存配置数据来为数据库设定种子。

注意
本快速入门中使用的方法用于简化identityserver的启动和运行。您应该设计适合您的体系结构的数据库创建和维护策略。

startup.cs中添加此方法以帮助初始化数据库:

private void initializedatabase(iapplicationbuilder app)
{
    using (var servicescope = app.applicationservices.getservice<iservicescopefactory>().createscope())
    {
        servicescope.serviceprovider.getrequiredservice<persistedgrantdbcontext>().database.migrate();

        var context = servicescope.serviceprovider.getrequiredservice<configurationdbcontext>();
        context.database.migrate();
        if (!context.clients.any())
        {
            foreach (var client in config.getclients())
            {
                context.clients.add(client.toentity());
            }
            context.savechanges();
        }

        if (!context.identityresources.any())
        {
            foreach (var resource in config.getidentityresources())
            {
                context.identityresources.add(resource.toentity());
            }
            context.savechanges();
        }

        if (!context.apiresources.any())
        {
            foreach (var resource in config.getapis())
            {
                context.apiresources.add(resource.toentity());
            }
            context.savechanges();
        }
    }
}

上面的代码可能需要将这些命名空间添加到您的文件中:

using system.linq;
using identityserver4.entityframework.dbcontexts;
using identityserver4.entityframework.mappers;

然后我们可以从configure方法中调用它:

public void configure(iapplicationbuilder app)
{
    // this will do the initial db population
    initializedatabase(app);

    // the rest of the code that was already here
    // ...
}

现在,如果运行identityserver项目,则应创建数据库并使用快速入门配置数据进行种子设定。您应该能够使用sql server management studio或visual studio来连接和检查数据。

注意
上面的initializedatabase辅助api可以方便地为数据库设定种子,但是这种方法并不适合每次运行应用程序时执行。填充数据库后,请考虑删除对api的调用。

15.7 运行客户端应用程序

您现在应该能够运行任何现有的客户端应用程序并登录,获取令牌并调用api – 所有这些都基于数据库配置。

注意
本节中的代码仍然依赖于config.cs及其虚构用户alice和bob。如果您的用户列表很简短且静态,则调整后的config.cs版本可能就足够了,但您可能希望在数据库中动态管理更大且更流畅的用户列表。asp.net identity是一个需要考虑的选项,下一节的快速入门列出了此解决方案的示例实现。

github地址