从壹开始 [ Ids4实战 ] 之五 ║ 多项目集成统一认证中心的思考
前言
哈喽大家好,好久都没有写文章了,这次又重新开始写技术文章了,半年前我还是一直保持每周都写文章的,后来是为了响应群友的号召,开始踏上了录制视频(https://www.bilibili.com/video/av58096866),直播授课(https://live.bilibili.com/21507364)的道路,目前看来效果还算基本及格吧,虽然人气没有那些大佬的多,不过我也是一直很尽心力,包括字幕的录入,每周三定期的讲课,不过真的很累,但是自认为做的还可以了,只是没能申请上MVP,今年唯一的遗憾🙃。
编者按:
很多小伙伴说,学不懂授权认证,搞不懂 IdentityServer4 ,看不懂文章啥意思。当然具体怎么采用还是看所在公司的技术能力上限,但是从个人未来知识规划出发,这些知识点还是需要考虑的,有益无害。
为什么突然想要写文章了呢,我是有强迫症的,目前开源了五个系列的文章,从 NetCore 到 Vue,从 Admin 到 Nuxt,然后从 DDD 到 IdentityServer4,我自认为都是贴近实战的,因为看到某些小伙伴说,应用到了公司的项目里,我也是很开心,而且 NetCore 项目的Github start 数也破千了,我感谢每一个点赞评论的小伙伴,我都是每条评论必回,每个邮件也必回:
我自信这一年多以来,对netcore的基础知识已经了解的七七八八了,基本都能回答些,但是每当有人问到 IdentityServer4 的相关内容的时候,我都稍微捉襟见肘,虽然我写了四篇文章,但是因为中间卡了一个心结(具体内容下文会说明),所以没有进行下去,也让自己没有进一步去研究这个框架的动力了,大家肯定都懂那个心理,如果一直有个刺,然后时间长了会刻意回避它。
不过最后我还是决定要搞起来,既然说到了要布道,就要走下去,所以我会除了之前的四篇文章外,还再有三到四篇的文章来开启 Ids4 的第二阶段的学习,然后配合还有大概八个视频的讲解,如果你看过我的第一个系列视频教程,应该心里有谱,我第二个系列讲的怎么样,所以我也就不多说了,这两周准备一下,下周末开启视频讲解第二系列。
那下面我们就马上开启今天的这篇文章吧。
零、回顾与目标
还记得我开启 IdentityServer4 讲解是在半年前,当时我本想是八篇文章,将我的所有项目 —— 主要是 Blog.Admin 和 Blog.Vue 这两个项目统一集成到 Ids4 的网关授权上来,当然有精力也可以把 DDD 和 Nuxt 的认证也添加进来,只不过 DDD 已经用了 Identity 了,这个优先级可以暂时往后放放,然后就写了四篇文章,主要是偏重于实战,而不是讲解那老生常谈的知识点:
但是在写到了第四篇文章的时候,出现了一个说小不小,说大不大的问题,让我搁置了整整半年,是什么问题,我简单说说:
我看博客园很多博主在讲 Ids4 的时候,基本情况都是用的无状态的认证方式,什么意思呢?
1、就是应用场景都是对应的 [Authorize] 这种只要登录了就可以的项目里,没有涉及到复杂的策略授权,比如角色和模块;
2、然后你可能也会说,“ 我看到他们文章讲解中生成的 token 中,可以自定义 Role 到 Claim 里呀,你直接在 Blog.Core 项目获取到 Role 进行策略授权不就行了”,你说的没错,但是这里有一个后续问题;
3、那我的 Role 信息在哪里管理?是在 Blog.Ids4 (认证服务)项目,还是在 Blog.Core (业务资源服务)项目里?
这里你可以稍微暂停一下,暂停五分钟,自己想一想,如何去解决这些问题,特别是第三个问题。
如果你把 Role 的信息管理放到了 Blog.Ids4 项目里,那 Blog.Core 如何针对 Role 进行匹配 Url进行授权配置呢?
但是如果把 Role 管理放到了 Blog.Core 项目里,那就没有办法根据 UName 和 Pwd 生成 token 的时候,在 Claims 里生成 Role 信息了。
这个并没有那么简单哟,当然如果你们公司开发过,可以直接看下文,和我想的方案对比下,如果有出入,或者你的更好,我热烈欢迎提出批评和指导建议,好了,那我就说说我的三种思路吧。
一、无状态的简单授权认证
这种方案呢,刚刚我们在上文已经说到了,我也已经实现了,就是在文末我的项目中,已经实现这个了, 就是 Blog.Ids4 只单单提供认证功能,不用管 Role 的相关声明 Claims,基本都是对应的前台项目,比如我们的博客项目和电商项目,用户登录了就行,就可以直接发文章或者购买商品了,不用理睬具体啥页面不能访问,这个就不多说了,很好理解。
这种场景,很好解决,认证服务项目和资源服务项目 相互独立,没有交际,只是提供一个认证网关的作用,这里就不多说了。
但是这种情况有时候并不是很多,或者说是少数的,我们更多的,还是需要在 token 中携带 role 的 claim 声明,特别是后台管理系统,都是有状态的授权,比如我们的 Blog.Core 是比较复杂的策略授权,那这个时候就需要讨论一下数据库的问题了,其实重点还是讨论 Role 表是在哪个项目维护的问题。
二、有状态的授权认证中心
那么问题就来了,我们为了满足复杂的策略授权,就需要在 Blog.Core 项目里将 Role 和 Url/Api 进行匹配映射到数据库,那就必须用 Role 表。
但是我们上边也讨论很多了,我们必须要在 token 中携带 Role 的Claim,那就必须放到 Blog.Ids4 项目里维护 Role。
这个时候是不是发现两种情况很矛盾,为了达到这个项目,我简单看了看 ABP 项目,这个还是群管理 @Kawhi 提的建议,我得到了一丝丝的想法,ABP项目是把认证和资源服务合并到了一起!
1、认证数据库与业务数据库合二为一
这个我最终决定采用的方案,这个方案已经放弃,我现在Blog.Core资源服务器已经支持多库了,可以不用合并了,
具体代码我已经慢慢的在 Ids4 项目里迁移修改了,感兴趣的可以看看,其实说白了,就是把 EFCore 生成表结构的数据库,指向 Blog.Core 项目的就行了,然后这个时候,我们就看到了两个 Role 表:
那既然我们的 Ids4 项目用的是 AspNetRoles 来进行认证的,我们直接用这个表不就好了?!,舍弃下边的 Role 表,用上边 Ids4 自带的 AspNetRoles 表,来和我们的 Module 和 Permission 表就行授权映射匹配,这样是不是完美的解决了我们上边出现的所有问题!
然后我们的 User 管理和 Role 管理,都移交到了 Ids4 项目里,从而使得我们的 Blog.Coe 资源服务器只需要踏踏实实的做授权就行了:
这个不用担心看不懂,我在后边的文章和视频中也会详细说到,本文主要说的是这个思路,具体业务逻辑和代码,我会详细讲解的,你能看懂我的意思就行。
这样是不是很好的解决了 Role 管理的问题,而且可以快速的将 Blog.Admin 和 Vue 项目给切换过来,很简单。但是,万事就怕但是😂,这里有一个问题,就是如果我有多个资源服务器怎么办,多个资源服务器肯定是不能和一个认证数据库合并的,比如我的项目结构是这样的:
这种我们是不好去把 资源服务器和 认证中心的数据库放到一起的,当然这种微服务的方案也不是这么玩儿的,我以后会继续说到这种情况,如何来设计。
咱们先不说微服务,就说单一服务,那如果不能数据库合并,又想保证 Role 的良好维护,我想了不是很好的办法,一共分下边几种情况。
2、认证DB与资源DB分离,两个Role表
既然不放到一起,那就分离开,还是我目前项目 Github 上的情况,两个数据库,两个 Role 表 ,但是呢,对 Role 的管理,还是放到 Ids4 服务器,
只不过这个时候,需要在资源服务器 Blog.Core 里,写一个定时器或者服务,将 Ids4 Db 中的 AspNetRoles 表数据,统一导入到 Blog.Core Db 的 Roles 表中,然后我们在资源服务器还是来操作 Roles 表,基本我的资源服务器修改的代码很少,只是多了一个数据同步的方法而已,
相对于上边的方案,这个优点是:
1、代码改动小;
2、多个资源服务器可以共存,共用一个认证中心;
3、防止因为 IdentityServer4 官方更新,而破坏我们的资源服务代码,侵入性小。
缺点也是有的,就是比较 low,而且不能保证实时性,更新速度受到定时器的限制。那和老李讨论一下,他给的建议是,可以在不使用表同步,而使用接口的形式,请往下看。
3、认证DB与资源DB分离,一个Role表,通过接口调用
这种方案,最终被我 PASS 了,思路我简单说下,其实很简单,就是我们的资源服务器 Role 表弃用,整个项目统一用 Ids4 认证服务器提供一个对外暴露的 GetRoles 的 api 接口,来保证实时性和统一性,
但是这样缺点太明显,受官网的限制,而且对资源服务器的侵入性太强,尽管可以封装一个方法。
说这个的目的,只是想让大家集思广益,开拓思维,学习都是思考的过程。
4、【推荐】认证DB和资源DB分离,一个Role表,多库操作
这个方案是有条件的,资源服务器必须是支持多库操作的,目前我的Blog.Core项目已经支持了多库操作,配置一下,就可以获取 Blog.Idp 项目中的 Role 信息了,是很方便的。
但是如果你的资源服务器不支持多库操作,那我还是建议使用第一种方案(数据库合并),和第三种吧(通过 idp 项目提供一个role的接口)。
5、分离 Role 分布式服务管理中心
这个是一个不愿透露姓名的群友的建议,我本来想放到其他方案里,但是感觉也是很不错的,这里单放出来,我个人也没有考虑到他的这一点 —— 抽离出 Role 管理中心,做分布式服务给 认证中心。
具体来说是什么意思呢,我这里继续画一个图就知道了:
这样不仅可以解决 Role 同步问题,还能很好的进行扩展,我们无论增加多少个 资源服务器,只需要向 Role 管理中心,推送相应的Role就行,然后我们的 Ids4 认证中心,只需要从 Role 管理中心获取,你可以通过资源服务器的 domain 来做区分,来管理 User。
三、其他方案
具体的请看下边的评论哟,评论也很精彩!!!
除了我上边提出来的一些方案,如果你有什么想法,或者好的注意,可以文章下边留言或者评论,不胜感激,比如:
1、@老张说:不在资源服务器里进行数据库的映射匹配,全部写到Controller控制器的特性上的角色或模块授权[Authorize(Roles = "Admin,Client")],这样也能解决。
2、@大家说:这里我就不写名字了,评论区的小伙伴较多的还是采用【拆分服务,合并数据库】的模式,这样代码也不会乱,数据也很好同步效果。
期待你的热评!