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

sty*_*yix 5 authentication authorization oauth openid-connect microservices

我尝试将Oauth2/OpenId Connect实现为基于Java/Spring Cloud的微服务架构.我目前的问题是关于微服务之间的令牌传播或者像RabbitMQ这样的消息代理.

很少有话题谈论这个.我只发现了这个Stackoverflow线程,但我不喜欢提出的答案.

以下是不同的情况:

我的微服务A接收最终用户通过API网关发起并携带有效访问令牌的请求(JWT具有与最终用户相对应的范围/声明:用户名,ID,电子邮件,权限等).这种情况没有问题.微服务具有处理请求的所有信息.

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

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

==>如果令牌在到达微服务B之前到期会发生什么?

  • 第二种解决方案:使用OAuth(aka服务帐户)提出的"客户端凭证授权".这意味着微服务A请求具有自己凭据的新访问令牌并使用它来调用微服务B.

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

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

对于这个cas,我​​听说过来自OAuth 2 的" 令牌交换 "草案.这个想法非常有趣.它允许微服务A将用户访问令牌转换为另一个具有较少权限的访问令牌,但是为微服务A伪造.不幸的是,这种机制仍处于草案中,并未在许多产品中实现.

第二个问题:如果微服务A向RabbitMQ发送消息并且微服务B收到此消息,会发生什么?

  • 第一个解决方案:身份验证和授权由RabbitMQ管理(虚拟主机,帐户等)

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

  • 第二个解决方案:像第一个问题一样,使用"客户端凭据授予"

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

提前致谢.

use*_*461 0

这非常简单。始终有两个用例,我们将其称为最终用户和 app2app。总是要兼顾两者。

考虑一个简化的架构:

         Internet                
User ----------------> Service A +-------> Service B
      HTTP request               |
                                 +-------> Service C
Run Code Online (Sandbox Code Playgroud)

假设用户通过令牌进行身份验证。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 <--------------+
Run Code Online (Sandbox Code Playgroud)

传递 JWT 用户令牌不适用于不适用于最终用户的中间件。特别是数据库和队列。

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

So service B and service C need to get functional accounts with write permission respectively to the SQL database and the Kafka Queue.

Services X,Y,Z need to read from the queue, they also each need a dedicated read-only access. They don't strictly need to have a JWT user token per message, they could trust that what's in the queue is intended, because whatever wrote to the queue in the first place must have had explicit permission to write to the queue.

It gets a bit tricky if service X needs to call yet another HTTP service that requires a user token. That's hard to do if the token wasn't forwarded. It would be possible to store the token in the queue along the message, but it's really ill advised. One does not want to save and persist tokens everywhere (the kafka queue writes messages and would basically become a highly-sensitive token database, erf). That's where the forwarding of user tokens shows its limits, at the boundaries of systems speaking different protocols with different access control models. One has to think carefully of how to architecture systems together to minimize this hassle. Use dedicated service accounts to interface specific systems that don't understand end-user tokens or don't have end-user tokens.