IdentityServer4 学习笔记-使用EntityFramework Core

833 阅读3分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第5天,点击查看活动详情

1.EntityFramework

首先要移动到数据库有两种类型的数据:

  • 配置数据
  • 操作数据

配置数据是有关资源和客户端的配置信息。操作数据是IdentityServer在使用时生成的信息,例如令牌,代码,这些存储使用接口来完成,我们在IdentityServer4.EntityFramework.Storage Nuget包中提供接口的EF实现。

注册EF实现的扩展方法包含在IdentityServer4.EntityFramework Nuget包中(引用 IdentityServer4.EntityFramework.Storage)。

从项目中添加对IdentityServer4.EntityFramework Nuget包的引用:

cd quickstart/src/IdentityServer
dotnet add package IdentityServer4.EntityFramework
2. 使用的SqlServer

IdentityServer4.EntityFramework.Storage包中包含从IdentityServer的模型映射实体类。作为IdentityServer模型变化,所以会在实体类IdentityServer4.EntityFramework.Storage。当使用IdentityServer4.EntityFramework.Storage并随着时间的推移升级时,将数据库架构以及实体类更改时该架构所需的更改。管理这些更改的一种方法是使用EF迁移

3. 配置存储

接下来将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。也可以看出,这是使用连接字符串的位置。

调用将MigrationsAssembly用于通知EF将包含迁移代码的主机项目(因为它与包含DbContext类的程序集不同)。

4. 添加迁移

创建迁移,在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文件夹,并且包含新创建的迁移的代码。

5.初始化数据库

定义的内存配置数据来为数据库设定种子数据。

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来连接和检查数据。

6.修改客户端配置

没有太多必要的修改,先允许客户端使用混合流,此外我们还希望客户端允许执行不在用户上下文中的服务器到服务器API调用(这与我们的客户端凭证快速启动非常相似)。这是使用该AllowedGrantTypes属性表示的。

new Client
{
    ClientId = "mvc",
    ClientName = "MVC Client",
    AllowedGrantTypes = GrantTypes.Hybrid,

    ClientSecrets =
    {
        new Secret("secret".Sha256())
    },

    RedirectUris           = { "http://localhost:5002/signin-oidc" },
    PostLogoutRedirectUris = { "http://localhost:5002/signout-callback-oidc" },

    AllowedScopes =
    {
        IdentityServerConstants.StandardScopes.OpenId,
        IdentityServerConstants.StandardScopes.Profile,
        "api1"
    },
    AllowOfflineAccess = true
};
7.使用访问令牌

OpenID Connect处理程序会自动保存令牌(访问和刷新)。这就是SaveTokens设置的作用。

cookie检查视图迭代这些值并在屏幕上显示,令牌存储在cookie的属性部分中。访问的最简单方法是使用Microsoft.AspNetCore.Authentication命名空间中的扩展方法。

var accessToken = await HttpContext.GetTokenAsync("access_token")
var refreshToken = await HttpContext.GetTokenAsync("refresh_token");

使用访问令牌访问API,需要检索令牌,并在HttpClient上设置:

public async Task<IActionResult> CallApi()
{
    var accessToken = await HttpContext.GetTokenAsync("access_token");

    var client = new HttpClient();
    client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
    var content = await client.GetStringAsync("http://localhost:5001/identity");

    ViewBag.Json = JArray.Parse(content).ToString();
    return View("json");
}

创建json.cshtml 视图:

<pre>@ViewBag.Json</pre>