一、前言

在前面的篇章介绍中,一些基础配置如API资源、客户端资源等数据以及使用过程中发放的令牌等操作数据,我们都是通过将操作数据和配置数据存储在内存中进行实现的,而在实际开发生产中,我们需要考虑如何处理数据持久化呢?

这时「IdentityServer4」具有良好的扩展性,其中一个可扩展点是用于「IdentityServer」所需数据的存储机制,进行持久化操作。

下面将如何配置「IdentityServer」以使用「EntityFramework」(EF)作为此数据的存储机制把这些数据存储到「Sql Server」数据库, 这样更符合我们实际生产环境的需求。

二、初识

在我们的 「IdentityServer4」中官方定义的两个上下文,是有两种类型的数据需要持久化到数据库中:

1、「配置数据」(资源、客户端、身份);//这里是对应配置上下文 ConfigurationDbContext

2、「IdentityServer」在使用时产生的 「操作数据」(令牌,代码和用户的授权信息consents);//这里是对应操作上下文 PersistedGrantDbContext

「这两个上下文以及对应的数据模型,已经被 IdentityServer4 官方给封装好了」, 我们不需要做额外的操作,直接进行迁移即可使用。

2.1 ConfigurationDb

ConfigurationDbContext (IdentityServer configuration data) —— 负责数据库中对客户端、资源和 CORS 设置的配置存储;

如果需要从 EF 支持的数据库加载客户端、标识资源、API 资源或 CORS 数据 (而不是使用内存中配置), 则可以使用配置存储。此支持提供 IClientStoreIResura StoreICorsPolicyService 扩展性点的实现。这些实现使用名为 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  configvar 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 securebuilder.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 PersistedGrantDbContext4、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 操作数据库.

解决方法 : 可参考上面的实践部分中的「数据库上下文」.

  1. 获取数据库连接字符串

  2. 配置数据库服务

4.2 dotnet ef 无法执行

因为找不到指定的命令或文件

从 3.0 起,EF Core 命令列工具 (dotnet ef) 不在 .NET Core SDK 里面,需另装。命令如下:

dotnet tool install --global dotnet-ef

五、总结

  1. 简单介绍了「IdentityServer4」持久化存储机制相关配置和操作数据,实现了数据迁移,及应用程序的实践。

  2. 本篇未对用户进行持久化操作存储说明,因为「IdentityServer4」本就支持了接入其他认证方式,所以自己根据需要进行合理「扩展」的,比如我们可以使用 「Asp.Net Core 自带的 Identity」 身份认证机制来「实现扩展」,当然,你也可以自己定义相应的操作,在后续篇章中会进行说明介绍。

  3. 如果有不对的或不理解的地方,希望大家可以多多指正,提出问题,一起讨论,不断学习,共同进步。

  4. 项目地址

https://github.com/i3yuan/Yuan.IdentityServer4.Demo/tree/main/DiffAuthMode/EntityFrameworkStorage

六、附加

「EF支持持久化配置和操作数据」

IdentityServer4系列 | 支持数据持久化相关推荐

  1. docker 保存 环境持久化_Docker深入浅出系列 | 容器数据持久化

    Docker深入浅出系列 | 容器数据持久化 Docker已经上市很多年,不是什么新鲜事物了,很多企业或者开发同学以前也不多不少有所接触,但是有实操经验的人不多,本系列教程主要偏重实战,尽量讲干货,会 ...

  2. 重启redis命令_redis系列之——数据持久化(RDB和AOF)

    在数据库(如mysql)和缓存(如redis)的发展中,都会相互借鉴对方的长处来弥补自身的不足.比如mysql作为持久化数据库,为了提高数据的访问速度,会使用缓存技术,当一条sql查询完成后,mysq ...

  3. 给expvarmon插上数据持久化的“翅膀”

    1. expvar包与expvarmon Go在标准库中为暴露Go应用内部指标数据提供了标准的对外接口,这就是expvar包[1].expvar包通过init函数将内置的expvarHandler(一 ...

  4. 安装kube-prometheus项目:k8s部署prometheus、监控k8s核心组件、添加告警(微信、钉钉、企业微信)、进行数据持久化

    概述 很多地方提到Prometheus Operator是kubernetes集群监控的终极解决方案,但是目前Prometheus Operator已经不包含完整功能,完整的解决方案已经变为kube- ...

  5. 【redismemcached】数据类型、内存管理、数据持久化和集群管理的区别

    这几年redis很火,redis也常常被当做memcached的挑战者被提到桌面上来.关于redis和memcached的比较比比皆是.然而,redis真的在功能.性能以及内存使用效率上都超越memc ...

  6. redis的基本操作And数据持久化方式以及redis实现mybatis缓存

    Redis 1.NoSql # NoSql(Not Only Sql),不仅仅是sql,泛指非关系型数据库 2.NoSql的诞生 随着互联网web2.0网站的兴起,传统的关系型数据库在高并发和特大规模 ...

  7. Redis和Memcached的区别(数据类型、内存管理、数据持久化、集群管理)

    Redis的作者Salvatore Sanfilippo曾经对这两种基于内存的数据存储系统进行过比较: Redis支持服务器端的数据操作:Redis相比Memcached来说,拥有更多的数据结构和并支 ...

  8. 老司机带你玩转面试(1):缓存中间件 Redis 基础知识以及数据持久化

    引言 今天周末,我在家坐着掐指一算,马上又要到一年一度的金九银十招聘季了,国内今年上半年受到 YQ 冲击,金三银四泡汤了,这就直接导致很多今年毕业的同学会和明年毕业的同学一起参加今年下半年的秋招,这个 ...

  9. Redis数据持久化机制及数据恢复

    什么是数据持久化? 我们知道Redis是基于内存的非关系型数据库,对数据的增删改查操作都直接在内存中进行,再加上Redis不用维护数据之间的"关系",导致它性能很高,速度很快. 但 ...

最新文章

  1. jquery和javascript的区别(转载自脚本之家)
  2. linux shell 文件比较 diff 简介
  3. word2vec原理(二):基于Hierarchical Softmax的模型
  4. iOS-控件响应用户控制事件之事件处理
  5. SAP Spartacus界面看不到Carousel左右移动控件的一个可能原因
  6. Elasticsearch嵌套查询
  7. 传智播客 Html基础知识学习笔记
  8. Canvas 超详细
  9. Java :反射详解
  10. 【Luogu1631】序列合并(优先队列)
  11. [debug] 令人智熄的调试:忘记加return
  12. Visual Studio 2013/2015/2017快捷键(转)
  13. Python网络爬虫
  14. 如何删除win10更新文件并彻底解决win10自动更新问题?
  15. Ubuntu20.04 系统搭建 NetBox(开源 IPAM/DC 管理工具)
  16. ES的创建索引和文档操作
  17. epoll与reactor模式
  18. 保姆式教学:用Tableau制作盒须图(箱线图)
  19. 给你的数据加上杠杆:文本增强技术的研究进展及应用实践
  20. 《Parallel batch k-means for Big data clustering》 SCI (聚类k-means)

热门文章

  1. linux守护进程的编写
  2. php函数的初步使用
  3. Java设计模式----策略模式(Strategy)
  4. jquery获取文档高度和窗口高度的例子
  5. diff和patch工具使用(转)
  6. GDB调试多进程|多线程程序
  7. [WP] 关于页面切换动画 记录
  8. 关于IE某些原因导致下拉列表弹出阻止窗口的解决办法
  9. Membership学习记录
  10. 219. 单页应用 会话管理(session、cookie、jwt)