如何在微服务架构中使用 3rd 方 IDP 实现 OpenID Connect 身份验证

Posted

技术标签:

【中文标题】如何在微服务架构中使用 3rd 方 IDP 实现 OpenID Connect 身份验证【英文标题】:How to implement OpenID Connect authentication with 3rd party IDPs in a microservices architecture 【发布时间】:2018-12-16 02:04:27 【问题描述】:

在过去的 10 多天里,我阅读了所有关于理解 OAuth2 和 OpenID Connect 的内容,但发现很多人不同意实施,这让我很困惑。

据我了解,我找到的所有文章和示例都假设您想访问例如。谷歌日历,个人资料信息或电子邮件,如果你例如。用谷歌登录,但我不需要访问除我自己的 API 之外的其他东西——我只想使用谷歌、Facebook 等登录,并获得一个我可以在我自己的数据库中链接到我的用户的 ID——仅此而已.

我将尝试说明我的用例并将其用作示例。

图表上的一个注释:身份验证服务可能会内置到 API 网关中——我对这个例子并不重要,因为这不是关于“在哪里做”,而是“如何做”最好的方法”,对于像我这样的架构,它用于我自己的 API/微服务,而不是访问 Google、Facebook 等外部 API

如果您能理解我想用上图说明的内容,请告诉我是否误解了这一点。

您在此处看到的此架构的最基本要求是:

用户可以使用谷歌、Facebook等登录。 所有微服务都将使用相同的登录名 OpenId 用户将在数据库中拥有一个关联的帐户 用户访问权限在我自己的数据库中定义,基于组、角色和权限

我不打算在用户通过身份验证并登录后使用外部 API。不需要访问用户日历、电子邮件等。所以我真的只需要身份验证部分而不需要其他任何东西(成功登录的证明)。所有用户访问都在我自己的数据库中定义。

所以我想到了几个基本问​​题。

首先,OpenID Connect 是否是仅用于身份验证的正确工具(我不会使用授权,因为除了从身份验证)? 人们通常不同意是使用 ID 还是访问令牌来访问您自己的 API。据我了解,ID 令牌仅用于客户端(用户代理),而访问令牌用于例如。访问谷歌日历、电子邮件等...... OpenID 提供者的外部 API......但由于我只会访问我自己的 API,我是否需要访问令牌或 ID 令牌 - 保护的正确方法是什么您自己的 API?

如果 ID 令牌真的只是用于客户端,那么它可以显示例如。当前登录的用户,没有去数据库,我有 0 使用它,因为我可能会从数据库中查询用户并将其存储在我的反应前端应用程序的 redux 中。

困境:将用户详细信息、组、角色和权限存储在 JWT 中还是不用于 API 授权?

通过仅将用户标识符存储在令牌中,这意味着我始终允许具有有效令牌的经过身份验证的用户在授权之前调用端点,然后首先根据 db 查询结果和我自己的数据库中的权限确定访问权限. 通过在 JWT 中存储更多关于用户的数据,这意味着在某些情况下,我可以在访问 API 之前进行授权/访问(组、角色、权限)检查 - 只有用户信息才有可能,组,角色和权限存储在登录时发出的 JWT 中。在某些情况下,由于例如,这是不可能的。 CMS 内容访问权限在每个节点级别。但这仍然意味着更好的性能。

如图所示,我通过网关发送所有 API 请求,网关(本身或使用身份验证服务)将不透明的访问令牌转换为带有标识符的 JWT,因此我可以识别用户在图形数据库中 - 然后验证用户是否具有所需的组、角色和权限 - 不是来自外部 API,而是来自我自己的数据库,如图所示。

这似乎对每个请求都进行了大量工作,即使服务可以共享 JWT,以防多个服务需要相互交叉调用。

始终查找用户及其在数据库中的权限的好处自然是,当用户访问级别发生更改时,他会立即被拒绝/授予访问权限,并且始终保持同步。如果我将用户详细信息、组、角色和权限存储在 JWT 中并将其保存在客户端本地存储中,我想这可能会造成安全问题,并且很难更新用户信息、组、角色和权限在那个 JWT 里面?

在 JWT 中存储用户访问级别和信息的一大优势当然是,在许多情况下,我可以阻止用户调用某些 API,而不必在查找数据库后确定访问权限。

因此,整个令牌转换意味着以性能为代价提高安全性,但通常推荐并且值得吗?或者在 JWT 中存储用户信息和组、角色、权限是否足够安全?

如果是,我是否将我自己的数据库中的所有信息存储在 ID 令牌、访问令牌或第三个令牌中 - 将什么令牌发送到 API 并确定是否应授予用户访问给定资源的权限他在数据库中的权限?如果我不需要与 ID 提供者 API 交互,我真的需要访问令牌吗?或者我是否将我的所有组、角色、权限存储并附加在由 OpenID connect 发布的 ID 令牌(对我来说似乎并不干净)中,并调用 API 并使用它授权我自己的 API 端点,即使有人说你永远不应该使用 ID 令牌来访问 API?还是创建一个新的 JWT 来存储从我的数据库中获取的所有信息,用于决定用户是否可以访问给定的资源/API 端点?

请不要只链接到一般规格或一般信息,因为我已经阅读了所有内容 - 我只是不明白如何将所有这些信息应用于我的实际用例(上图)。尽量做到具体一点。

再次尝试简单的流程:

【问题讨论】:

您是在实现 API 管理器还是会使用现有的管理器? 我不确定您所说的“API 管理器”是什么意思,但用高级语言制作一个非常基本的 API 网关/反向代理(使用一些库)应该相当容易例如 Go (golang),这是我将使用的。 如何/何时在数据库中创建用户?您从哪里获得用户详细信息? 我猜是最正常的方式。我计划使用相同的登录框执行此操作 - 唯一的区别是如果找不到“链接”用户(通过电子邮件或某些标识符?)它将被创建,其中包含来自 Google/Facebook 登录的电子邮件和第一个/最后一个来自身份验证的名称。因此,实际上在第一次使用 Google/Facebook 等登录时,获取最基本的用户详细信息将很有用。但再也不会,在创建用户之后。 【参考方案1】:

以下答案仅适用于具有 3rd 方 IDP(如 Google)的 OpenID Connect 身份验证流程。它不适用于您托管自己的 IDP 的架构。

(有一些 API 网关(例如 Tyk 或 Kong)支持开箱即用的 OpenID Connect。)

您可以使用 JWT(ID 令牌)来保护您的 API。然而,这有一个缺点。 JWT 不能轻易撤销。

我不建议这样做。相反,您应该实现一个 OAuth2 授权服务器,该服务器为您的 API 颁发访问令牌。 (在这种情况下,您有两个 OAuth2 流。一个用于身份验证,一个用于授权。来自 IDP 的 ID 和访问令牌仅用于身份验证。)

下图是API网关和认证/授权服务器是两个独立的服务的设置。(如上所述,认证/授权也可以由API网关完成。)

身份验证流程(授权代码授予)调用标记为蓝色。授权流(隐式授予)调用标记为绿色。

1:您的网络应用是从应用服务器加载的。

2a:用户点击您的登录按钮,您的 Web 应用构建授权 URL 并打开它。 (见:Authorization Request)

2b:由于用户尚未通过身份验证并且与您的授权服务器没有有效会话,因此他想要访问的 URL 被存储,并且您的授权服务器响应重定向到其登录页面。

3:登录页面从您的授权服务器加载。

4a:用户点击“使用...登录”。

4b:您的授权服务器构建 IDP 授权 URL 并通过重定向响应它。 (见:Authentication Request)

5a:IDP 授权 URL 已打开。

5b:由于用户尚未通过身份验证并且与 IDP 没有有效会话,因此存储了他想要访问的 URL,并且 IDP 以重定向到其登录页面的方式进行响应。

6:登录页面从 IDP 加载。

7a:用户填写他的凭据并点击登录按钮。

7b:IDP 检查凭据,创建新会话并通过重定向到存储的 URL 进行响应。

8a:再次打开IDP授权URL。

(此处省略审批步骤。)

8b:IDP 创建授权并通过重定向到您的授权服务器的回调 URL 进行响应。 (见:Authentication Response)

9a:回调URL打开。

9b:您的授权服务器从回调 URL 中提取授权码。

10a:您的授权服务器调用 IDP 的令牌端点,获取 ID 和访问令牌并验证 ID 令牌中的数据。 (见:Token Request)

(10b:如果某些需要的声明在 ID 令牌中不可用,您的授权服务器会调用 IDP 的用户信息端点。)

11a/b:您的授权服务器在您的服务/数据库中查询/创建用户,创建一个新会话并通过重定向到存储的 URL 进行响应。

12a:再次打开授权URL。

(此处省略审批步骤。)

12b/+13a/b:您的授权服务器创建/获取授权(创建访问令牌)并响应重定向到您的 Web 应用的回调 URL。 (见:Access Token Response)

14a:回调 URL 已打开。

14b:您的网络应用从回调 URL 中提取访问令牌。

15:您的网络应用进行 API 调用。

16/17/18:API 网关检查访问令牌,与 JWT(包含用户信息,...)交换访问令牌并转发调用。

也可以设置授权服务器调用 API 网关。 在这种情况下,授权完成后,授权服务器将访问令牌和 JWT 传递给 API 网关。然而,在这里,每次用户信息发生变化时,授权服务器都必须“通知”API 网关。

【讨论】:

谢谢 - 这将非常有帮助!不仅对我,对许多其他人也是如此。我猜这种令牌交换发生在内部,其中访问令牌被交换为 JWT,这意味着将为来自客户端的每个 API 调用创建 JWT,对吧?这意味着每个进入网关的请求也会从数据库中查询 17 (get_user_roles)。这样,虽然从性能的角度来看不是最有效的,但在安全性和灵活性方面也是我认为最好的——因此控制权保留在网关/身份验证服务中。 是的,将为每个 API 调用创建 JWT。 (您可以向 API 网关添加缓存以提高性能。稍微旧(约 10 秒)的用户详细信息可能是可以容忍的。) 发送auth-server的id-token可以吗?【参考方案2】:

这是一个很长的问题。但我相信大部分都可以通过下面的回答来总结,

据我了解,我找到的所有文章和示例都假设您想访问例如。谷歌日历,个人资料信息或电子邮件,如果你例如。用谷歌登录,

您不一定要使用Access token(在某些情况下是ID token)来访问token issuer提供的服务。您可以通过自己的API来使用token。这些身份提供者(授权服务器的同义词,或简称 IDP)用于保存最终用户的身份。例如,典型的互联网有一个 Facebook 帐户。使用 OAuth 和 OpenID Connect,同一用户可以使用您的 API 或任何 OAuth/OIDC 接受的服务。这减少了为最终用户创建的用户配置文件。

在企业域中,OAuth 和 OIDC 的用途相同。拥有一个 Azure AD 帐户可以让您使用 MS Word,并且 Azure AD 的 OIDC 将发布令牌,这些令牌可用于对支持基于 OIDC 的身份验证的内部 API 或第三方 ERP 产品(用于组织)进行授权。希望现在清楚了

图表上的一个注释是,身份验证服务可能会内置到 API 网关中 - 不确定这是否会更好?

如果您计划实施 API 网关,请三思。如果事情是小规模的,如果你认为你可以维持它,那就继续吧。但是考虑一下可以提供大部分所需功能的 API 管理器。欢迎您阅读这篇文章about WSO2 API manger 并了解它的功能(不,我不是为他们工作)。

例如,API 管理器为 OAuth 和 OIDC 内置了身份验证处理机制。它可以通过一组简单的配置来处理 API 身份验证。有了这样的解决方案,您就摆脱了实现一切的要求。

如果您不能使用 API 管理器而必须自己做怎么办

OpenID Connect 用于身份验证。您的应用程序可以验证 id 令牌并验证最终用户。要通过 API Gateway 访问 API,我认为您应该使用 Access 令牌。

要验证访问令牌,您可以使用身份提供者的introspection endpoint。要获取用户信息,您可以使用 user-info 端点。

一旦访问令牌被验证,API 网关可以在有限的时间内创建会话(理想情况下小于或等于访问令牌的生命周期)。后续请求应与此会话一起被 API 网关接受。或者,您仍然可以使用经过验证的访问令牌。由于您在第一次调用时对其进行了验证,因此您可以缓存一段时间,从而避免往返验证。

要验证用户详细信息、权限和其他授权,您必须将用户绑定到会话,或者在令牌验证时关联用户以从 API 网关访问令牌。我对此也不是很清楚,因为我不知道您的数据库逻辑是如何工作的。

【讨论】:

感谢您的长篇回答。不幸的是,我没有得到我希望的澄清。例如,“如果你不能使用 API 管理器并且必须自己做怎么办”之前的所有内容并没有真正帮助我更深入地理解“如何仅使用 openid connect 进行身份验证 - 如果你只需要为你的身份验证自己的 API / 微服务架构”。我不想使用现有的 API 管理器/网关,而是构建一个非常简单的,就像您在图中看到的那样。我会尽量让我的问题更清楚,所以答案会更具体。这太抽象了:-) @Dac0d3r 好吧,我认为您需要一位解决方案架构师来讨论并充分理解您的方案。祝你好运:)【参考方案3】:

首先感谢您在此论坛中写下一个非常有价值的问题的耐心 我们也有同样的情况和问题 我想通过,因为我们公司详细屏蔽了图像

试图与书中引用的类似内容进行比较 实践中的高级 API - Prabath Siriwerdena [第 269 页]联合访问 API 的章节。绝对值得一读他的作品

API GW 应向 IDP 调用令牌交换 OAUTH2.0 配置文件 [前提是 IDP 应支持 OAUTH 2.0 的令牌交换配置文件

API Gateway 的缺失,将导致相当定制的开发 用于每个 API 检查的访问控制 您可以在每个 api 或微服务上进行此检查 [作为可重用代码为您工作的库] 肯定会成为瓶颈。]

【讨论】:

我也在阅读高级 API 安全书籍,以了解 - 当您的 IDP 是代理或外部 IDP 案例时 OAUTH2.0 所说的内容,您的访问令牌仅适用于 Scope openid 电子邮件电话地址,因为这些只是您可以从 IDP 请求的用户范围 @mattke - 尊敬的先生,您的图表上的查询很少 提到了两个访问令牌步骤 10a - 获取访问令牌作为 IDP 的一部分 [这只是用于读取配置文件/用户属性的访问令牌],步骤12a/13b 有另一个令牌,这个令牌与步骤 10a 不同,是谁创建的, IDP 或 Authorization Server 。它如何将步骤 10a 令牌和这个令牌关联起来。最后还有一个由 API Gateway 通过调用授权服务器创建的令牌, - 你能详细解释一下,谁创建的令牌

以上是关于如何在微服务架构中使用 3rd 方 IDP 实现 OpenID Connect 身份验证的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Node-RED 中使用 3rd 方 oauth 服务器包含单点登录?

3rd 方组件许可在 WPF 中如何工作? [关闭]

Android apk - 如何使用 gradle 从 3rd 方依赖项中排除 .so 文件

GCM 如何使用 GCM 和 3rd 方服务器注销设备

Apollo 服务器多个 3rd 方 API

在 iOS 上使用 phonegap 构建推送通知,没有 3rd 方服务