转载

微服务体系中 OAuth2 协议内部服务落地方案探讨——仅将网关视为 Resource Server 的认证/授权架构

关于OAuth2协议, 相信有一定开发经验的开发者也不陌生了。

毕竟只要涉及到 多端应用、第三方登陆、API接口调用 总会接触到这个概念。

这里就主要探讨一下在微服务体系中使用 OAuth2 协议实现鉴权方案时,如何最大化减少网络通信次数,从而提升系统的响应速度。

准备讨论的架构是我个人对于 OAuth2 这个协议在微服务下比较推崇的实现方案, 但由于博主经验尚浅 难免可能有考虑不周的地方,如果有什么错误、或者说更值得优化的点 欢迎指出。

先来看一个最基本的微服务架构:

微服务体系中 OAuth2 协议内部服务落地方案探讨——仅将网关视为 Resource Server 的认证/授权架构

不要吐槽为什么少了服务治理中间件、服务之间通信的流程之类的。这里只是展示一个最基本的模型,为了方便理解

那么从上图可以看出, 一个微服务架构是有着所谓 网关层 的, 网关层的作用是解决微服务难点之 :  服务这么多,如何访问?

每一个请求都会试图请求网关层, 网关层根据 请求路径/请求来源/请求域名 之类的差别来执行不同的路由策略, 从而将请求路由到特定的服务上。

微服务体系下最普遍的 OAuth2 落地方案:

OAuth2 这个东西他所谓的 资源服务器(Resource Server)、认证服务器(Authorization Server) 这两个概念, 如果是在微服务架构下落地的话。

网关层还是那个网关层,只是单纯的负责路由。

提供服务的应用作为资源服务器。 在单独开设一个认证服务器 专门用来进行认证。

请求流程大概长成这样:

微服务体系中 OAuth2 协议内部服务落地方案探讨——仅将网关视为 Resource Server 的认证/授权架构

其实这样实现也没有问题。 我看各种支持OAuth2的框架你要是默认使用他推荐的方法的话, 你系统架构起来就长成这样子。

他会将你的每一个微服务作为资源服务器, 从而对所有的请求进行权限的效验。  如果没有携带凭据访问资源,就会将你重定向到认证服务器进行认证。

你若是携带了凭据,那么在你访问资源前 资源服务器还会去请求认证服务器的 CheckToken 端点,来检查你的权限是否可以访问对应的资源 (这个流程我这没画)

如果你对性能比较敏感的话, 就会发现这样子的架构是有缺点的。

  1.  所有的服务都要引入 OAuth2 相关的库, 耦合对应的处理模板。增加了包体积大小。
  2.  检查请求是否拥有资源的访问权限时,总是会去访问认证服务的检查端点,给认证服务增加负担同时降低了请求的效率
  3.  自己的系统并非第三方服务,为了获取凭证还是得去走授权码模式那套繁琐的操作重定向来重定向去

一个微服务体系下性能更高的 OAuth2 落地方案:

看到上面所说的缺点,拆开来分析其痛点所在

既然说每个服务都要引用 OAuth2 相关的库不好,那么能否将 OAuth2 的影响范围降到更低?

资源服务器检查Token有效性时总会去走一道HTTP请求,能不能将这个请求消除掉?

认证时太繁琐,既然自己服务都在一个内网里,还有必要让用户重定向到认证服务器的页面登陆么?

那么为了解决这些痛点,我在这提出一个架构方案:

  • 鉴权流程放在网关层 ,  系统中只有一个 Resource Server ——即网关。
  • 既然 Authorization Server 和 Resource Server 都是微服务系统下的一个服务,那么可以使用一个共同的 TokenStore 来储存Token
  • 用户的登陆操作,沿用系统本身的用户名密码登陆
    • 用户只需提交用户名密码,请求登陆接口即可。
    • 系统底层作为一个 OAuth2 Client 请求 Authorization Server (密码模式) 得到 AccessToken 和 RefreshToken
    • 全程在用户无感知的情况下获取了AccessToken返回给用户, 浏览器中只有一个请求操作
    • 用户在请求资源时, 直接在网关层获取 Token 信息 (不走 CheckToken 端点) 并实现鉴权流程; 无任何 HTTP 交互,实现高速鉴权
  • 同时 Authorization Server 授权码模式的接口保留,对第三方登陆功能无影响

该架构的大体活动图如下所示: 

微服务体系中 OAuth2 协议内部服务落地方案探讨——仅将网关视为 Resource Server 的认证/授权架构

可以看到整个系统中只有一处在鉴权, 那就是网关服务。

而其他的都是资源提供者 (包括认证服务)

更具体的中间件选择:

那么架构方案有了, 就到如何落地了。

在这个架构落地之前还有一个要点, 那就是关于权限控制。

众所周知, 权限的控制在 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 协议内部服务落地方案探讨——仅将网关视为 Resource Server 的认证/授权架构

这样就是一个微服务体系中完整的 OAuth2 内部服务落地方案了。

虽说这个图画的也不算特别完整, 看客们可以自行将其想象成每个节点都是集群的,包括微服务网关。  微服务网关前面还有 Nginx 集群, Nginx集群前面还有F5……

原文  https://www.skypyb.com/2020/04/jishu/1517/
正文到此结束
Loading...