关于OAuth2协议, 相信有一定开发经验的开发者也不陌生了。
毕竟只要涉及到 多端应用、第三方登陆、API接口调用 总会接触到这个概念。
这里就主要探讨一下在微服务体系中使用 OAuth2 协议实现鉴权方案时,如何最大化减少网络通信次数,从而提升系统的响应速度。
准备讨论的架构是我个人对于 OAuth2 这个协议在微服务下比较推崇的实现方案, 但由于博主经验尚浅 难免可能有考虑不周的地方,如果有什么错误、或者说更值得优化的点 欢迎指出。
不要吐槽为什么少了服务治理中间件、服务之间通信的流程之类的。这里只是展示一个最基本的模型,为了方便理解
那么从上图可以看出, 一个微服务架构是有着所谓 网关层 的, 网关层的作用是解决微服务难点之 : 服务这么多,如何访问?
每一个请求都会试图请求网关层, 网关层根据 请求路径/请求来源/请求域名 之类的差别来执行不同的路由策略, 从而将请求路由到特定的服务上。
OAuth2 这个东西他所谓的 资源服务器(Resource Server)、认证服务器(Authorization Server) 这两个概念, 如果是在微服务架构下落地的话。
网关层还是那个网关层,只是单纯的负责路由。
提供服务的应用作为资源服务器。 在单独开设一个认证服务器 专门用来进行认证。
请求流程大概长成这样:
其实这样实现也没有问题。 我看各种支持OAuth2的框架你要是默认使用他推荐的方法的话, 你系统架构起来就长成这样子。
他会将你的每一个微服务作为资源服务器, 从而对所有的请求进行权限的效验。 如果没有携带凭据访问资源,就会将你重定向到认证服务器进行认证。
你若是携带了凭据,那么在你访问资源前 资源服务器还会去请求认证服务器的 CheckToken 端点,来检查你的权限是否可以访问对应的资源 (这个流程我这没画)
如果你对性能比较敏感的话, 就会发现这样子的架构是有缺点的。
看到上面所说的缺点,拆开来分析其痛点所在
既然说每个服务都要引用 OAuth2 相关的库不好,那么能否将 OAuth2 的影响范围降到更低?
资源服务器检查Token有效性时总会去走一道HTTP请求,能不能将这个请求消除掉?
认证时太繁琐,既然自己服务都在一个内网里,还有必要让用户重定向到认证服务器的页面登陆么?
那么为了解决这些痛点,我在这提出一个架构方案:
可以看到整个系统中只有一处在鉴权, 那就是网关服务。
而其他的都是资源提供者 (包括认证服务)
那么架构方案有了, 就到如何落地了。
在这个架构落地之前还有一个要点, 那就是关于权限控制。
众所周知, 权限的控制在 OAuth2 协议规范里是完全由 Authorization Server 来把控的,换成了这样子的架构在权限这块就需要动一点脑筋。
我这里仅提出我的思路:
OAuth2 Client 相关的信息、用户信息、RBAC 权限模型, 这些东西还是保存在关系型数据库中(MySQL/Oracle/Postgres), 交由 Authorization Server 全权把控
AccessToken、RefreshToken 存储在 Redis 之中, 由 Authorization Server 和 Gateway (Resource Server) 共享。
用 Redis 缓存 Token 信息和相对应的用户信息、用户持有的角色信息本身就是经典架构了。 基于其强大的数据结构, 构建分布式信号量、令牌桶之类的来在网关处限流也是很好用的。
权限控制最重要的还是资源信息, 比如 Rest 资源是否可以被用户访问。 网关处必须清楚一个请求试图访问的资源有着什么样的权限要求。用户的角色是否匹配的上,如果可以的话最好还要支持动态的权限调整。
而本身微服务架构就会有一个中间件来做服务治理, 这里假设这个中间件是 Zookeeper 。那么我们就可以使用 Zookeeper 的持久节点+ Watch机制来实现这个需求。
当资源服务启动时就会去连接 Zookeeper 将自己这个服务可以提供的所有 Rest 资源注册进去 (如果node value 中没有的话) , 网关服务在启动时直接获取指定 Znode 的 value ,拷贝到自己内存中,后续的请求鉴权就直接在内存中进行,即可不进行任何网络 IO 操作实现鉴权功能。
同时网关服务在启动时向对应的权限节点注册 Watch 。这样子在后续有权限动态变更 (比如网页端管理后台更新了权限) 时, 只要节点信息被修改,就会收到对应的通知 从而更新自己内存中的拷贝。
这样就是一个微服务体系中完整的 OAuth2 内部服务落地方案了。
虽说这个图画的也不算特别完整, 看客们可以自行将其想象成每个节点都是集群的,包括微服务网关。 微服务网关前面还有 Nginx 集群, Nginx集群前面还有F5……