哈喽你们周三好,今天终于又从新开启 IdentityServer4 的落地教程了,很少说,既然开始了,就要努力作好😸。javascript
书接上文,在好久以前的上篇文章《二║ 基础知识集合 & 项目搭建一》中,咱们简单的说了说 IdentityServer4 是如何调用和配置 Token 的,主要是一些入门的基础知识和概念,也算是第一次尝鲜了,其实 Ids4 自己没有那么神秘,咱们只须要知道它是一个丰富的认证框架,又具备很好的可扩展性,核心就是如何配置某些客户端经过该受权服务来包括另外一些资源服务器,供用户使用。由于中间隔了很长时间了,这里简单的回顾下上篇文章说了什么,下边这几个小问题,相信你们脑中都能很快的有一些概念,至少是脸熟了,若是不是很清楚,请翻看上边的连接,或者官网再看看:html
一、什么是资源?什么是客户端?什么是令牌 ?vue
二、IdentityServer4 主要是受权仍是认证 ?java
三、OAuth 2.0、OpenID、OpenID Connect三者的关系 ?git
四、Ids4 依赖的 Nuget 包是什么 ?github
五、如何快速开发 UI ?sql
今天这篇文章主要是操做和探索相结合的一篇,我看网上不少教程,基本上是按照官网概念的知识点,配合着 Github 开源地址,讲解一遍,知识是懂了,但是要是本身开发起来,仍是有不少的疑惑,甚至是根本走不下去,我就从初学者的立场出发,换一个思路,简单的对其进行探索,但愿能让初学者感兴趣,而不会失去学习的冲动,固然,这篇文章不会一会儿就很完整,须要一段时间的沉淀和完善,我会不定时将学习到的感悟一一补充下来,而后也会和别人讨论的心得写下来,慢慢的将这篇文章逐渐完整起来。但愿你们都积极评论,混个脸熟也会挺好,若是一直潜水,可能都不知道你的存在。😂shell
以前的配置都是在内存中,下面将如何把这些数据存储到Sql Server数据库, 这样更符合生产环境的要求。好啦,立刻开始今天的内容~~~数据库
(这是简单处理了下登陆页,要用就要好看些)json
咱们要把全部的数据都存储下来,采用接口进行建模,咱们在 IdentityServer4.EntityFramework
Nuget 包中提供这些接口的 EF 实现。
算上咱们上篇文章中引用的 IdentityServer4 包,一共这三个 Nuget 依赖环境:
这一节你们若是看懂的话,必定会对 IdentityServer4 丰富性和可扩展性 有特别切身的了解和体会!😁
为何要多个上下文?
还记得在第二个系列 DDD 中,咱们说到了多个上下文的用途,单个数据库能够有多个上下文。例如, 若是您的数据库包含多个数据库架构, 而且您但愿将每一个架构做为单独的自包含区域处理, 则此功能很是有用。在使用多个上下文类型时,您还会看到其余问题, 例如共享实体类型及其从一个上下文传递到另外一个上下文等。通常来讲,设计多个上下文,会使您的设计更干净, 并分离不一样的功能区域,但同时它的成本也增长了复杂性。框架之因此会设计成多个上下文,是为了更好的扩展,咱们也能够部署到不一样的物理机数据库中,更好的实现独立性。
在咱们的 IdentityServer4-EF 框架中,有三个上下文,准确来讲是 2+1 个上下文,为何这么说呢 ?
由于第三个上下文不是官方定义的,是咱们本身根据须要进行合理扩展的,好比咱们可使用 Asp.Net Core 自带的 Identity 身份认证机制来实现扩展,固然,你也能够本身定义相应的操做,若是你还不懂,就想象一下咱们在 Blog.Core 项目中,咱们不是本身定义的基于 JWT 的认证受权么( 用户表 + 角色表 + 关系表等等 ),可是若是你看个人 DDD 项目的话,应该能够看到我又使用了 Core 官方自带的 Identity 机制,毕竟结合 EFCore 咱们能够很快的实现用户数据的管理,而不用本身造轮子了。
我们这里先说说前两个上下文,在咱们使用 Ids4 的时候,有两种类型的数据须要持久化到数据库中:
一、配置数据(资源、客户端、身份);//这里是对应配置上下文
ConfigurationDbContext
二、IdentityServer在使用时产生的 操做数据(令牌,代码和用户的受权信息consents);//这里是对应操做上下文
PersistedGrantDbContext
这两个上下文以及对应的数据模型,已经被 IdentityServer4 官方给封装好了, 咱们不须要作额外的操做,直接进行迁移便可。
ConfigurationDbContext (IdentityServer configuration data) —— 负责数据库中对客户端、资源和 CORS 设置的配置存储;
若是须要从 EF 支持的数据库加载客户端、标识资源、API 资源或 CORS 数据 (而不是使用内存中配置), 则可使用配置存储。此支持提供 IClientStore、IResura Store 和 ICorsPolicyService 扩展性点的实现。这些实现使用名为 ConfigurationDbContext 的 dbcontext 派生类对数据库中的表进行建模。
具体在 startup 中如何配置,下面会说到。
PersistedGrantDbContext (IdentityServer operational data.) -—— 负责存储赞成、受权代码、刷新令牌和引用令牌;
若是须要从 EF 支持的数据库 (而不是默认的内存数据库) 加载受权授予、赞成和令牌 (刷新和引用), 则可使用操做存储。此支持提供了 IPersistedGrantStore 扩展点的实现。实现使用名为 PersistedGrantDbContext 的 dbcontext 派生类对数据库中的表进行建模。
ApplicationDbContext 继承 IdentityDbContext (Entity Framework database context used for identity.) —— 负责与 asp. net 标识相关的用户;
这个上下文,就是 IdentityServer4 体现 可扩展性 的关键点,为何说是可扩展的,由于这是咱们本身定义的,你能够在这里仅仅定义一个User表,也能够像咱们的 Blog.Core 项目中,定义三个表。咱们能够在这个上下文中,进行配置,来控制咱们的用户数据,固然这里看着很简单,是由于继承了 NetCore 官方的 Identity 了,可使用他们的那一套逻辑。
public class ApplicationUser : IdentityUser { //能够在这里扩展,下文会说到 } // 定义用户管理上下文,继承 NetCore 自带的 Identity 认证机制,也能够不继承而自定义表结构。 public class ApplicationDbContext : IdentityDbContext<ApplicationUser> { public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options) { } protected override void OnModelCreating(ModelBuilder builder) { base.OnModelCreating(builder); // Customize the ASP.NET Identity model and override the defaults if needed. // For example, you can rename the ASP.NET Identity table names and more. // Add your customizations after calling base.OnModelCreating(builder); } }
这个上下文,主要是用来处理咱们的用户数据相关的部分:
一、有多少用户数据和角色数据;
二、哪些用户,又拥有什么角色,又对应哪些Claims 声明。
这一块更像是咱们的平时的权限管理系统,就是在 User、Role、Claim 之间交互,文章下边会有一个数据模型图,会很清晰的看出来各自的对应关系。
咱们既然须要用到这几个上下文,就须要添加他们所对应的服务,别着急,往下看。
上边咱们说到了三个上下文,若是咱们直接执行迁移命令是会报错的,好比咱们直接迁移 PersistedGrantDbContext 上下文:
因此,就须要在项目中配置对应的服务,咱们在 startup.cs 启动文件中,配置服务 ConfigureService :
配置 EF 操做数据库,很简单,这个就按照平时咱们使用的正常的方法便可:
一、获取数据库链接字符串:
( 我这里使用的是读取文件的形式,其实就是字符串放到文件里了,你们本身根据须要作相应调整便可,我这么是防止密码泄露。)
二、配置数据库服务:
var builder = services.AddIdentityServer(options => { options.Events.RaiseErrorEvents = true; options.Events.RaiseInformationEvents = true; options.Events.RaiseFailureEvents = true; options.Events.RaiseSuccessEvents = true; })
// Configures IdentityServer to use the ASP.NET Identity implementations of IUserClaimsPrincipalFactory,
// IResourceOwnerPasswordValidator, and IProfileService. Also configures some of
// ASP.NET Identity's options for use with IdentityServer (such as claim types to
// use and authenticaiton cookie settings).
.AddAspNetIdentity<ApplicationUser>() // 添加配置数据(客户端 和 资源) .AddConfigurationStore(options => { options.ConfigureDbContext = b => b.UseSqlServer(connectionString, sql => sql.MigrationsAssembly(migrationsAssembly)); }) // 添加操做数据 (codes, tokens, consents) .AddOperationalStore(options => { options.ConfigureDbContext = b => b.UseSqlServer(connectionString, sql => sql.MigrationsAssembly(migrationsAssembly)); // 自动清理 token ,可选 options.EnableTokenCleanup = true; });
而后是咱们的用户数据存储,其实咱们能够根据本身的须要,本身创建对应的表,好比咱们 Blog.Core 项目中,咱们定义了用户、角色和关系表等数据库接口,可是这样的话,可能少一些状况,因此通常状况下,都是采用的 asp.net core 自带的 Identity 身份验证,并且对扩展性很友好,就好比下文咱们就对用户主表作了自定义扩展,继承了 IdentityUser, ApplicationUser : IdentityUser
// 数据库配置系统应用用户数据上下文
services.AddDbContext<ApplicationDbContext>(options => options.UseSqlServer(connectionString)); // 启用 Identity 服务 添加指定的用户和角色类型的默认标识系统配置 services.AddIdentity<ApplicationUser, IdentityRole>() .AddEntityFrameworkStores<ApplicationDbContext>() .AddDefaultTokenProviders();
我这里提供了两种方法,至于哪一种更好,你们能够本身都试试,我都用过,可能第二种稍微多了一点,不过原理都是同样的。
1、迁移项目( 使用 Package Manager Console ):
一、add-migration InitialIdentityServerPersistedGrantDbMigration -c PersistedGrantDbContext -o Data/Migrations/IdentityServer/PersistedGrantDb 二、add-migration InitialIdentityServerConfigurationDbMigration -c ConfigurationDbContext -o Data/Migrations/IdentityServer/ConfigurationDb 三、add-migration AppDbMigration -c ApplicationDbContext -o Data 四、update-database -c PersistedGrantDbContext
五、update-database -c ConfigurationDbContext
六、update-database -c ApplicationDbContext
最后成功的生成了咱们须要的数据库:
判断是否支持命令行迁移,你能够在项目所在的目录下打开一个命令 Power shell 并运行命令 dotnet ef, 它应该是这样的:
我是在 PowerShell 中操做的:
//1.dotnet ef migrations add InitialIdentityServerPersistedGrantDbMigration -c PersistedGrantDbContext -o Data/Migrations/IdentityServer/PersistedGrantDb //2.dotnet ef migrations add InitialIdentityServerConfigurationDbMigration -c ConfigurationDbContext -o Data/Migrations/IdentityServer/ConfigurationDb //3.dotnet ef migrations add AppDbMigration -c ApplicationDbContext -o Data //4.dotnet run /seed
这里我就不一一操做了,就是普通的命令执行,生成的数据库如出一辙的。
可是要注意下,上边咱们在包管理控制台,仅仅是迁移了数据库,尚未进行数据 Seed 生成,不过你看 PowerShell 的命令中的第四步,我使用 dotnet run /seed 的命令,和上边的不同,没错,是由于下边我用了另外一种方法,经过代码的方法进行 Migrate 了,而且同时还 Seed 了数据,那具体如何操做呢,别慌,第三节我会说到了,如今这个时候,咱们已经把数据库的全部表都已经生成好了,先不着急同步数据,咱们先看看到底生成了哪些数据表。
我简单的作了下整个数据库的数据模型图,不一样模块用不一样的颜色区分,你们也能很清楚的看出来各自对应的关系,一目了然,其中配置数据又有三个部分,分别对应的是 客户端 + 资源 + 身份 。
接下来,我们针对这三个模块简单的说一说,由于这里尚未数据,提及来可能会比较抽象,这里先留一个坑,等配合 Vue 客户端使用了之后,再把完整数据展现出来,可能更好的理解了。
这是 Ids4 官方封装的第一部分——操做数据。PersistedGrants的数据,在登录时当你赞成请求许可的时候,就会在这个表里面添加一条数据。
举个栗子,我在 Blog.Vue 项目点击登陆,跳转到 BlogIdp 受权中心,认证成功后,跳转回来,就会在这个表中多了一条数据(这个功能我下次会开放出来,代码已经写好):
MMD4TtLkVH7sXIwFS/DLZRfLQnUga0XMrBDVuQZWfOw= | user_consent | c2841087-1b35-4ab4-afd6-f27f80c94e6c | blogvuejs | 2019-05-08 03:35:30.0000000 | NULL | {"SubjectId":"c2841087-1b35-4ab4-afd6-f27f80c94e6c","ClientId":"blogvuejs","Scopes":["openid","profile","roles","blog.core.api"],"CreationTime":"2019-05-08T03:35:30Z","Expiration":null} |
这一大块,是 Ids4 官方定义的第二模块 —— 配置数据,从这里咱们能够看出来 IdentityServer4 的另外一个特性——丰富性,上边我们说到了可扩展性,它提供了丰富的配置数据结构,从三个方面来给咱们定义好了这些数据,而不用咱们再去一个一个的去设计,去思考。
咱们能够经过这些表结构来存储咱们的服务数据,好比有哪些API资源(Blog.Core.API),哪些客户端资源,还有用到了哪些资源等等,若是你还不清楚的,能够想一想上篇文章中,咱们讲内存模式的 Ids4 的配置,就是这些数据,在下边的文章中,咱们会进行 Seed Data,那个时候咱们再进一步看下。
具体的数据你们能够看看,截个图:
这一块咱们就很清楚了,由于这是咱们自定义的用户数据(固然本项目是直接继承的 NetCore 的 Identity) ,光从数据库表名上,咱们就知道其中的含义了,就是用户角色管理。
下边我们会将 Blog.Core 项目中的用户信息导进来,先看看 AspNetUsers 表中是怎样的数据:
上边我们也简单的说了下数据库表结构,而后再结合三个上下文,你们应该都能明白各自的含义了,好啦,如今就是万事俱备,只欠数据了,是时候动手了!
还记得咱们上边在生成数据库的时候,我采用了两个方法,第一种很常规,那接下来就说说第二种中第四步( dotnet run /see ),是如何操做的。
首先,须要定义一个 SeedData 类,主要是用来 update-database 和 Seed Data 这两个做用,由于篇幅的缘由,具体的代码在 Github 上,你们自行下载查看便可:
上边我们也说到了,咱们经过继承使用 Identity 机制,来扩展了用户管理,很天然的,咱们须要用到 IdentityUser ,可是它的这个用户数据确定不能知足咱们的需求,因此,这里又一次体现了 Ids4 可扩展性的优势,咱们能够自定义用户主表:
// Add profile data for application users by adding properties to the ApplicationUser class public class ApplicationUser : IdentityUser { // 自定义属性 public string name { get; set; } public string RealName { get; set; } public int sex { get; set; } = 0; public int age { get; set; } public DateTime birth { get; set; } = DateTime.Now; public string addr { get; set; } public bool tdIsDelete { get; set; } }
这个功能是一个充分没必要要的条件,主要是为了解决某些项目先用了自身一套认证系统,而后又上了 IdentityServer4 项目的状况,就好比咱们的 Blog.Core 项目,就是这个状况。
那用户的数据从哪里拿到呢,很巧,你们还记得我在某一篇文章《 42 ║支持多种数据库 & 快速数据库生成》中,自动生成数据库的方案,就是这么使用的,从 Github 上直接拉取数据便可,这里整个派上用场,感受很不错,
private static string GitJsonFileFormat = "https://github.com/anjoy8/Blog.Data.Share/raw/master/Blog.Core.Data.json/{0}.tsv"; // 远程获取 Blog.Core 数据库的三表数据 var BlogCore_Users = JsonHelper.ParseFormByJson<List<sysUserInfo>>(GetNetData.Get(string.Format(GitJsonFileFormat, "sysUserInfo"))); var BlogCore_Roles = JsonHelper.ParseFormByJson<List<Role>>(GetNetData.Get(string.Format(GitJsonFileFormat, "Role"))); var BlogCore_UserRoles = JsonHelper.ParseFormByJson<List<UserRole>>(GetNetData.Get(string.Format(GitJsonFileFormat, "UserRole")));
我们在上篇文章中,定义了一个 InMemoryConfig 基于内存的配置文件,我们再进行完善下,应用到真实项目 Blog.Vue 中:
public class Config { // scopes define the resources in your system public static IEnumerable<IdentityResource> GetIdentityResources() { return new List<IdentityResource> { new IdentityResources.OpenId(), new IdentityResources.Profile(), new IdentityResource("roles", "角色", new List<string> { JwtClaimTypes.Role }), }; } public static IEnumerable<ApiResource> GetApiResources() { return new List<ApiResource> { new ApiResource("blog.core.api", "Blog.Core API") { // include the following using claims in access token (in addition to subject id) //requires using using IdentityModel; UserClaims = { JwtClaimTypes.Name, JwtClaimTypes.Role } } }; } // clients want to access resources (aka scopes) public static IEnumerable<Client> GetClients() { // javascript client return new List<Client> { new Client { ClientId = "blogvuejs", ClientName = "Blog.Vue JavaScript Client", AllowedGrantTypes = GrantTypes.Implicit, AllowAccessTokensViaBrowser = true, RedirectUris = { "http://localhost:6688/callback" }, PostLogoutRedirectUris = { "http://localhost:6688" }, AllowedCorsOrigins = { "http://localhost:6688" }, AllowedScopes = { IdentityServerConstants.StandardScopes.OpenId, IdentityServerConstants.StandardScopes.Profile, "roles", "blog.core.api" } } }; } }
经历了定义上下文 -> 数据库迁移 -> 获取数据 -> Seed Data 的过程后,就剩下最后一步了,执行操做:
很简单,我在 Blog.Core 项目中,采用的是 appsettings.json 文件配置的方法,这里换另外一种方法,Program.cs 控制台参数的形式
在上一讲中,咱们用到了官方的 快速启动 代码,里边已经包含了基本的页面,固然你也能够根据本身的状况去自定义,甚至采用静态的Ajax 来实现,我这里仍是使用的 MVC 结构,只不过套用了下一个简单样式(样式版权是http://www.uimaker.com/ 网站的,请不要作商业用途,注意产权),至少看起来好看了一些。
具体的登陆操做就不演示了,等下次把 增删改查 都作好了,再统一作下动图展现吧,具体如何设计,请听下回分解。
今天咱们简单的实现了对 IdentityServer4-EFCore 的相关配置,概念不是很难,为了加深印象,仍是提几个小问题,请你们多思考:
一、Ids4 一共用到了几个上下文,分别的用处是什么?
二、在迁移中,数据库生成了多少表,各个模块又是干什么的?
三、Ids4 的良好扩展性,体如今哪里?丰富性又体如今哪里?
四、ApplicationUser 类是起到什么做用的?
五、思考1:若是同一个用户有多个角色如何处理?
六、思考2:若是不使用 NetCore 自带的 Identity 认证,如何自定义上下文?
https://github.com/anjoy8/Blog.IdentityServer
---♥---♥--- ---♥---♥--- ---♥---♥---