使用 OAuth2/OpenId Connect 和微服务进行身份验证和授权

Posted

技术标签:

【中文标题】使用 OAuth2/OpenId Connect 和微服务进行身份验证和授权【英文标题】:Authentication and authorization with OAuth2/OpenId Connect and microservices 【发布时间】:2017-12-17 01:15:51 【问题描述】:

我尝试将 Oauth2/OpenId Connect 实现到基于 Java/Spring Cloud 的微服务架构中。我目前的问题是关于微服务之间或通过 RabbitMQ 等消息代理的令牌传播。

很少有话题谈论这个。我只找到了这个*** 线程,但我不喜欢建议的答案。

以下是不同的情况:

我的微服务 A 接收到由最终用户发起的请求,该请求通过 API 网关并携带有效的访问令牌(JWT 与最终用户对应的范围/声明:用户名、id、电子邮件、权限等)。这个案子没有问题。微服务拥有处理请求的所有信息。

第一个问题:如果微服务 A 需要调用微服务 B 会发生什么?

第一种解决方案:微服务 A 将访问令牌发送给微服务 B

==> 如果令牌在到达微服务 B 之前过期会怎样?

第二个解决方案:使用 OAuth(又名服务帐户)提出的“客户端凭据授予”。这意味着微服务 A 使用自己的凭据请求一个新的访问令牌,并使用它来调用微服务 B。

==> 使用此解决方案,与用户相关的所有数据(用户名、ID、权限等)都会丢失。

例如,微服务 B 中被调用的方法需要用户 id 才能工作。用户 id 值可以设置为查询字符串。 如果使用用户访问令牌调用该方法,则微服务 B 可以验证查询字符串中的用户 ID 值是否等于 JWT 令牌中的用户 ID 值。 如果使用服务访问令牌调用该方法,则微服务 B 无法验证查询字符串值,需要信任微服务 A。

对于这个案例,我听说了来自 OAuth 2 的“token-exchange”草案。这个想法非常有趣。它允许微服务 A 将用户访问令牌转换为另一个权限较少但为微服务 A 伪造的访问令牌。不幸的是,这种机制仍处于草案阶段,并没有在很多产品中实现。

第二个问题:如果微服务 A 向 RabbitMQ 推送消息,而微服务 B 收到此消息会怎样?

第一种解决方案:身份验证和授权由 RabbitMQ 管理(vhost、帐户等)

==> 再一次,所有与用户相关的数据都丢失了。此外,我们有 2 个存储库来管理身份验证和授权

第二个解决方案:和第一个问题一样,使用“客户端凭据授予”

你怎么看?还有其他更好的解决方案吗?

提前致谢。

【问题讨论】:

这有帮助吗:***.com/questions/29644916/… 和 nordicapis.com/… 感谢您的链接,但两者都在谈论一个基本用例,微服务之间没有通信(通过编排或编排) 您对这个问题有任何了解吗? 【参考方案1】:

这很简单。总有两个用例我们将称为最终用户和 app2app。总是要同时处理这两个问题。

考虑一个简化的架构:

         Internet                
User ----------------> Service A +-------> Service B
      HTTP request               |
                                 +-------> Service C

假设用户通过令牌进行身份验证。 JWT 或其他任何东西都没有关系。

服务 A 验证用户令牌并执行操作。它必须调用服务 B 和 C,以便发出适当的请求,并且必须在这些请求中包含用户令牌。

可能需要进行一些转换。也许 A 从 cookie 中读取用户令牌,但 B 从 Authorization: Bearer xxx 标头中读取令牌(HTTP API 中接受 JWT 令牌的常用方法)。也许 C 不是基于 HTTP 的,而是基于 GRPC(或任何开发人员现在使用的?),所以令牌必须通过该协议转发,不知道传递额外信息的一般做法是什么。

它相当简单,它适用于所有与最终用户打交道的服务,只要协议可以将消息/请求与其上下文多路复用。 HTTP 是一个理想的示例,因为每个请求都是完全独立的,Web 服务器可以根据路径和参数以及 cookie 等处理各种内容。

这也是一种极其安全的模型,因为操作必须由用户发起。想要删除用户帐户?该请求可以完全限于用户,不能只有员工/实习生/黑客致电https://internal.corp/users/delete/user123456https://internal.corp/transactions/views/user123456。实际上,例如客户支持可能需要访问这些信息,因此除了作为用户之外,还必须有一些有限的访问权限。

考虑一个真实的架构:

         Internet                
User ----------------> Service A +-------> Service B --------> SQL Database
      HTTP request               |
                                 +-------> Service C --------> Kafka Queue
                                                                    |
                                                                    |
                                           Service X <--------------+
                                           Service Y <--------------+
                                           Service Z <--------------+

传递 JWT 用户令牌不适用于不能在最终用户基础上工作的中间件。尤其是数据库和队列。

数据库不处理基于最终用户的访问(与队列相同的问题)。它通常需要专用的用户名/密码或 SSL 证书才能访问,这两者在该数据库实例之外都没有任何意义。每个表的访问是完全读/写或只读,没有精细权限的可能性(有一个行级权限的概念,但我们忽略它)。

所以服务 B 和服务 C 需要分别获取 SQL 数据库和 Kafka Queue 的写入权限的功能账户。

服务 X、Y、Z 需要从队列中读取,它们也都需要一个专用的只读访问权限。他们并不严格要求每条消息都有一个 JWT 用户令牌,他们可以相信队列中的内容是预期的,因为首先写入队列的任何内容都必须具有写入队列的明确权限。

如果服务 X 需要调用另一个需要用户令牌的 HTTP 服务,那就有点棘手了。如果没有转发令牌,这很难做到。可以将令牌与消息一起存储在队列中,但确实不建议这样做。不想到处保存和持久化令牌(kafka 队列写入消息,基本上会成为一个高度敏感的令牌数据库,erf)。这就是用户令牌的转发显示其局限性的地方,在系统的边界上使用不同的协议和不同的访问控制模型。人们必须仔细考虑如何将系统架构在一起以尽量减少这种麻烦。使用专用服务帐户来连接不了解最终用户令牌或没有最终用户令牌的特定系统。

【讨论】:

以上是关于使用 OAuth2/OpenId Connect 和微服务进行身份验证和授权的主要内容,如果未能解决你的问题,请参考以下文章

Spring Boot OAuth2/OpenID Connect Client 尝试解码 Jwt: Malformed Jwk set 时出错

OAuth2/OpenID Connect 保护 API 的自动化 API 测试

使用 Oauth2/OpenID 连接构建 Web-API

OIDC-基于OAuth2的下一代身份认证授权协议

Oauth2/Openid 连接。如何撤销未知的访问/刷新令牌

Web系统之常用单点登录协议