IdentityServer4系列 | 支持数据持久化
一、前言
在前面的篇章介绍中,一些基础配置如API资源、客户端资源等数据以及使用过程中发放的令牌等操作数据,我们都是通过将操作数据和配置数据存储在内存中进行实现的,而在实际开发生产中,我们需要考虑如何处理数据持久化呢?
这时IdentityServer4具有良好的扩展性,其中一个可扩展点是用于IdentityServer所需数据的存储机制,进行持久化操作。
下面将如何配置IdentityServer以使用EntityFramework(EF)作为此数据的存储机制把这些数据存储到Sql Server数据库, 这样更符合我们实际生产环境的需求。
二、初识
在我们的 IdentityServer4中官方定义的两个上下文,是有两种类型的数据需要持久化到数据库中:
1、配置数据(资源、客户端、身份);//这里是对应配置上下文
ConfigurationDbContext2、IdentityServer在使用时产生的 操作数据(令牌,代码和用户的授权信息consents);//这里是对应操作上下文
PersistedGrantDbContext
这两个上下文以及对应的数据模型,已经被 IdentityServer4 官方给封装好了, 我们不需要做额外的操作,直接进行迁移即可使用。
2.1 ConfigurationDb
ConfigurationDbContext (IdentityServer configuration data) —— 负责数据库中对客户端、资源和 CORS 设置的配置存储;
如果需要从 EF 支持的数据库加载客户端、标识资源、API 资源或 CORS 数据 (而不是使用内存中配置), 则可以使用配置存储。此支持提供 IClientStore、IResura Store 和 ICorsPolicyService 扩展性点的实现。这些实现使用名为 ConfigurationDbContext 的 dbcontext 派生类对数据库中的表进行建模。
2.2 PersistedGrantDb
PersistedGrantDbContext (IdentityServer operational data.) -—— 负责存储同意、授权代码、刷新令牌和引用令牌;
如果需要从 EF 支持的数据库 (而不是默认的内存数据库) 加载授权授予、同意和令牌 (刷新和引用), 则可以使用操作存储。此支持提供了 IPersistedGrantStore 扩展点的实现。实现使用名为 PersistedGrantDbContext 的 dbcontext 派生类对数据库中的表进行建模。
三、实践
3.1 新建站点
建立一个MVC的Asp.Net Core项目 ,使用MVC模板
3.2 Nuget包
IdentityServer4.EntityFramework以及EF相关包
1.IdentityServer4
2.IdentityServer4.AspNetIdentity
3.IdentityServer4.EntityFramework
因为本文中使用的是SqlServer数据库,所以需要安装对应的EF程序包对数据库的支持。
Microsoft.EntityFrameworkCore.SqlServer
3.3 数据库上下文
appsettings.json
  "ConnectionStrings": {
    "DataContext": "data source=.;initial catalog=Yuan.Idp;user id=sa;password=123456;",
  }
配置连接数据库
 var connectionString = Configuration.GetConnectionString("DataContext");
 if (connectionString == "")
 {
      throw new Exception("数据库配置异常");
 }
2.配置数据库服务
在startup.cs中ConfigureServices方法添加如下代码:
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddControllersWithViews();
            var connectionString = Configuration.GetConnectionString("DataContext");
            if (connectionString == "")
            {
                throw new Exception("数据库配置异常");
            }
            var migrationsAssembly = typeof(Startup).GetTypeInfo().Assembly.GetName().Name;
            // in DB  config
            var builder = services.AddIdentityServer(options =>
            {
                options.Events.RaiseErrorEvents = true;
                options.Events.RaiseInformationEvents = true;
                options.Events.RaiseFailureEvents = true;
                options.Events.RaiseSuccessEvents = true;
            }).AddConfigurationStore(options => //添加配置数据(ConfigurationDbContext上下文用户配置数据)
            {
                options.ConfigureDbContext = builder => builder.UseSqlServer(connectionString, sql => sql.MigrationsAssembly(migrationsAssembly));
            }).AddOperationalStore(options =>   //添加操作数据(PersistedGrantDbContext上下文 临时数据(如授权和刷新令牌))
            {
                options.ConfigureDbContext = builder => builder.UseSqlServer(connectionString, sql => sql.MigrationsAssembly(migrationsAssembly));
                // 自动清理 token ,可选
                options.EnableTokenCleanup = true;
                // 自动清理 token ,可选
                options.TokenCleanupInterval = 30;
            }).AddTestUsers(TestUsers.Users);
            // not recommended for production - you need to store your key material somewhere secure
            builder.AddDeveloperSigningCredential();
            services.ConfigureNonBreakingSameSiteCookies();
        }
3.4 迁移数据
3.4.1 控制台迁移
方法一:
需要添加EF工具,安装Microsoft.EntityFrameworkCore.Tools, 进行迁移
  1、add-migration InitialIdentityServerPersistedGrantDbMigration -c PersistedGrantDbContext -o Data/Migrations/PersistedGrantDb
   2、add-migration InitialIdentityServerConfigurationDbMigration -c ConfigurationDbContext -o Data/Migrations/ConfigurationDb
   3、update-database -Context PersistedGrantDbContext
   4、update-database -Context ConfigurationDbContext

3.4.2 在命令窗口
方法二:
判断是否支持命令行迁移,你可以在项目所在的目录下打开一个命令 Power shell 并运行命令 dotnet ef, 它应该是这样的:

dotnet ef 无法执行,因为找不到指定的命令或文件
从 3.0 起,EF Core 命令列工具 (dotnet ef) 不在 .NET Core SDK 里面,需另装。命令如下:
dotnet tool install --global dotnet-ef
要创建迁移,请在IdentityServer项目目录中打开命令提示符。 在命令提示符下运行这两个命令:
1. dotnet ef migrations add InitialIdentityServerPersistedGrantDbMigration -c PersistedGrantDbContext -o Data/Migrations/PersistedGrantDb
2. dotnet ef migrations add InitialIdentityServerConfigurationDbMigration -c ConfigurationDbContext -o Data/Migrations/ConfigurationDb
#生成
1. update-database -c PersistedGrantDbContext
2. update-database -c ConfigurationDbContext

3.5 显示数据库


(图片来自网络)
3.6 初始化数据库
在之前的篇章中,我们是定义的内存配置数据实现的操作,而在本篇中,我们进行数据持久化操作,可以将之前内存的数据作为种子处理迁移到创建的数据库中进行初始化操作。
参考文章: 用户数据迁移
3.6.1 创建文件
创建SeedData.cs文件,用于初始化基础数据:
    public class SeedData
    {
        public static void EnsureSeedData(IServiceProvider serviceProvider)
        {
            Console.WriteLine("Seeding database...");
            using (var scope = serviceProvider.GetRequiredService<IServiceScopeFactory>().CreateScope())
            {
                scope.ServiceProvider.GetService<PersistedGrantDbContext>().Database.Migrate();
               var context = scope.ServiceProvider.GetRequiredService<ConfigurationDbContext>();
                context.Database.Migrate();
                EnsureSeedData(context);
            }
            Console.WriteLine("Done seeding database.");
            Console.WriteLine();
        }
        private static void EnsureSeedData(ConfigurationDbContext context)
        {
            if (!context.Clients.Any())
            {
                Console.WriteLine("Clients 正在初始化");
                foreach (var client in Config.GetClients)
                {
                    context.Clients.Add(client.ToEntity());
                }
                context.SaveChanges();
            }
            if (!context.IdentityResources.Any())
            {
                Console.WriteLine("IdentityResources 正在初始化");
                foreach (var resource in Config.GetIdentityResources)
                {
                    context.IdentityResources.Add(resource.ToEntity());
                }
                context.SaveChanges();
            }
            if (!context.ApiResources.Any())
            {
                Console.WriteLine("ApiResources 正在初始化");
                foreach (var resource in Config.GetApiResources)
                {
                    context.ApiResources.Add(resource.ToEntity());
                }
                context.SaveChanges();
            }
            if (!context.ApiScopes.Any())
            {
                Console.WriteLine("ApiScopes 正在初始化");
                foreach (var resource in Config.GetApiScopes)
                {
                    context.ApiScopes.Add(resource.ToEntity());
                }
                context.SaveChanges();
            }
        }
    }
配置内容可以查看之前篇章内容文件Config.cs 或者项目地址.
3.6.2 调用方法
然后我们可以从主入口Main方法调用它:
        public static void Main(string[] args)
        {
            var seed = args.Contains("/seed");
            if (seed)
            {
                args = args.Except(new[] { "/seed" }).ToArray();
            }
            var host = CreateHostBuilder(args).Build();
            if (seed)
            {
                SeedData.EnsureSeedData(host.Services);
            }
            host.Run();
        }
3.6.3 程序运行
输入 dotnet run /seed 

3.6.4 效果

四、问题
4.1 提示找不到上下文
上面我们说到了的两个上下文,如果我们直接通过执行迁移命令是会报错的,比如我们直接迁移 PersistedGrantDbContext 上下文:

因为迁移的目标不匹配,需要更改迁移程序集,如
options.UseSqlServer(connection, b => b.MigrationsAssembly("Ids4.EFCore"))
所以,就需要在项目中配置对应的服务,我们在 startup.cs 启动文件中,配置服务 ConfigureService ,配置 EF 操作数据库.
解决方法 : 可参考上面的实践部分中的数据库上下文.
获取数据库连接字符串
配置数据库服务
4.2 dotnet ef 无法执行
因为找不到指定的命令或文件
从 3.0 起,EF Core 命令列工具 (dotnet ef) 不在 .NET Core SDK 里面,需另装。命令如下:
dotnet tool install --global dotnet-ef
五、总结
- 简单介绍了IdentityServer4持久化存储机制相关配置和操作数据,实现了数据迁移,及应用程序的实践。
 - 本篇未对用户进行持久化操作存储说明,因为IdentityServer4本就支持了接入其他认证方式,所以自己根据需要进行合理扩展的,比如我们可以使用 Asp.Net Core 自带的 Identity 身份认证机制来实现扩展,当然,你也可以自己定义相应的操作,在后续篇章中会进行说明介绍。
 - 如果有不对的或不理解的地方,希望大家可以多多指正,提出问题,一起讨论,不断学习,共同进步。
 - 项目地址
 
六、附加
IdentityServer4系列 | 支持数据持久化的更多相关文章
- Docker深入浅出系列 | 容器数据持久化
		
Docker深入浅出系列 | 容器数据持久化 Docker已经上市很多年,不是什么新鲜事物了,很多企业或者开发同学以前也不多不少有所接触,但是有实操经验的人不多,本系列教程主要偏重实战,尽量讲干货,会 ...
 - docker 系列 - 容器数据持久化和数据共享
		
docker 主要有两种数据存储形式, 一种是storage driver(也叫做 Graph driver), 另一种是 volume driver. stroage driver主要是存储那些无状 ...
 - Redis基本数据类型、数据持久化、过期策略及淘汰机制
		
一点技术.技术乐享!!! 如果有人问你:Redis这么快,他的“多线程模式”你了解吗? 请回答他:您是想问Redis这么快,为什么还是单线程模式吗? redis是什么 简单来说redis是C语言开发的 ...
 - 老司机带你玩转面试(1):缓存中间件 Redis 基础知识以及数据持久化
		
引言 今天周末,我在家坐着掐指一算,马上又要到一年一度的金九银十招聘季了,国内今年上半年受到 YQ 冲击,金三银四泡汤了,这就直接导致很多今年毕业的同学会和明年毕业的同学一起参加今年下半年的秋招,这个 ...
 - 认证授权:IdentityServer4 - 数据持久化
		
前言: 前面的文章中IdentityServer4 配置内容都存储到内存中,本篇文章开始把配置信息存储到数据库中:本篇文章继续基于github的代码来实现配置数据持久化到MySQL中 一.基于EFCo ...
 - Java 数据持久化系列之JDBC
		
前段时间小冰在工作中遇到了一系列关于数据持久化的问题,在排查问题时发现自己对 Java 后端的数据持久化框架的原理都不太了解,只有不断试错,因此走了很多弯路.于是下定决心,集中精力学习了持久化相关框架 ...
 - NoSql数据库Redis系列(3)——Redis数据持久化(RDB)
		
大家都知道 Redis 是一个内存数据库,所谓内存数据库,就是将数据库中的内容保存在内存中,这与传统的MySQL,Oracle等关系型数据库直接将内容保存到硬盘中相比,内存数据库的读写效率比传统数据库 ...
 - Java 数据持久化系列之池化技术
		
在上一篇文章<Java 数据持久化系列之JDBC>中,我们了解到使用 JDBC 创建 Connection 可以执行对应的SQL,但是创建 Connection 会消耗很多资源,所以 Ja ...
 - Java 数据持久化系列之 HikariCP (一)
		
在上一篇<Java 数据持久化系列之池化技术>中,我们了解了池化技术,并使用 Apache-common-Pool2 实现了一个简单连接池,实验对比了它和 HikariCP.Druid 等 ...
 
随机推荐
- WARN node unsupported "node@v6.11.2" is ......(windows系统更新node版本)
			
问题: 使用npm下载文件时报错:WARN node unsupported "node@v6.11.2" is incompatible with electron@^7.1.9 ...
 - VSCode 前端常用插件
			
1.Auto Close Tag 自动闭合HTML/XML标签 2.Auto Rename Tag 自动完成另一侧标签的同步修改 3.Beautify 格式化代码,值得注意的是,beautify插件支 ...
 - 详细了解 synchronized 锁升级过程
			
前言 首先,synchronized 是什么?我们需要明确的给个定义--同步锁,没错,它就是把锁. 可以用来干嘛?锁,当然当然是用于线程间的同步,以及保护临界区内的资源.我们知道,锁是个非常笼统的概念 ...
 - LeetCode-022-括号生成
			
括号生成 题目描述:数字 n 代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且 有效的 括号组合. 示例说明请见LeetCode官网. 来源:力扣(LeetCode) 链接:https ...
 - LeetCode-059-螺旋矩阵 II
			
螺旋矩阵 II 题目描述:给你一个正整数 n ,生成一个包含 1 到 \(n^{2}\) 所有元素,且元素按顺时针顺序螺旋排列的 n x n 正方形矩阵 matrix . 示例说明请见LeetCode ...
 - Hystrix&Dashboard配置使用
			
目录 Hystrix是什么 熔断 什么是熔断 熔断类型 打开 半开 关闭 使用方法 导包 添加启动注解 新增方法 测试 降级 什么是降级 使用方法 导包 修改yml,新增如下 启动类新增注解 @Ena ...
 - hive从入门到放弃(一)——初识hive
			
之前更完了<Kafka从入门到放弃>系列文章,本人决定开新坑--hive从入门到放弃,今天先认识一下hive. 没看过 Kafka 系列的朋友可以点此传送阅读: <Kafka从入门到 ...
 - 【一周聚焦】 联邦学习 arxiv 2.16-3.10
			
这是一个新开的每周六定期更新栏目,将本周arxiv上新出的联邦学习等感兴趣方向的文章进行总结.与之前精读文章不同,本栏目只会简要总结其研究内容.解决方法与效果.这篇作为栏目首发,可能不止本周内容(毕竟 ...
 - Linux  swap分区操作
			
swap交换分区是系统RAM的补充,swap 分区支持虚拟内存.当没有足够的 RAM 保存系统处理的数据时,会将数据写入 swap 分区,当系统缺乏 swap 空间时,内核会因 RAM 内存耗尽而终止 ...
 - find: `./folder': No such file or directory 错误处理
			
这是我正在处理的目录的内容: misha@hp-laptop:~/work/c/5$ ls -l total 8 -rw-rw-r-- 1 misha misha 219 May 20 15:37 d ...