如何在微服务架构中使用第三方IDP实施OpenID Connect身份验证

Dac*_*d3r 8 oauth-2.0 jwt openid-connect microservices

在过去的十多天里,我看了看过的所有关于OAuth2和OpenID Connect的内容,但发现很多人不同意实现,这确实使我感到困惑。

据我了解,我发现的所有文章和示例都假设您要访问例如。Google日历,个人资料信息或电子邮件(例如)用google登录,但是除了我自己的API外,我不需要访问其他任何东西-我只想使用Google,Facebook等进行登录,并获取一个可以链接到自己数据库中的用户的ID-仅此而已。

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

在此处输入图片说明

图表上的注释:身份验证服务可能内置在API网关中-在本例中我并不重要,因为这与“在哪里做”无关,而是“如何以最佳方式做”,适用于诸如我的架构之类的架构,该架构用于我自己的API /微服务,而不访问Google,Facebook等。外部API

如果您可以理解上面这张图说明的内容,请告诉我是否误解了。

您在此处看到的对该体系结构的最基本要求是:

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

在用户进行身份验证和登录后,我不打算使用外部API。不需要访问用户的日历,电子邮件等。因此,我只需要身份验证部分,而无需其他任何东西(成功登录的证明)。所有用户访问权限都在我自己的数据库中定义。

因此想到了一些基本问题。

  • 首先,OpenID Connect是否仅是用于身份验证的正确工具(我将没有使用授权,因为除了从身份验证中获取ID外,我不需要对google / facebook API的读/写访问权限)?
  • 人们通常不同意使用ID还是访问令牌访问自己的API。据我了解,ID令牌仅用于客户端(用户代理),而访问令牌则用于。正在访问Google日历,电子邮件等。。。OpenID Provider的外部API。但是由于我将仅访问自己的API,因此我是否需要访问令牌或ID令牌-正确的保护方法是您自己的API?

如果ID令牌确实仅用于客户端,则可以显示例如。当前登录的用户,无需进入数据库,我有0个用途,因为我可能会从数据库中查询用户并将其存储在redux中以供我的React前端应用程序使用。

难题:要将用户详细信息,组,角色和权限存储在JWT内还是不进行API授权?

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

如您在图上看到的,我正在通过网关发送所有API请求,这将(本身或通过身份验证服务)将不透明的访问令牌转换为带有标识符的某些JWT,因此我可以在图中识别用户数据库-然后验证用户是否具有必需的组,角色和权限-不是通过外部API,而是从我自己的数据库中获取,就像您在图中看到的那样。

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

始终查找用户及其在db中的权限的好处自然是,一旦用户访问级别发生变化,他就立即被拒绝/授予访问权限,并且始终保持同步。如果我将用户详细信息,组,角色和权限存储在JWT中,并将其保留在客户端本地存储中,则我认为这可能会构成安全问题,并且很难更新用户信息,组,角色和权限在那个JWT里面?

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

因此,整个令牌翻译意味着以性能为代价提高安全性,但是通常建议这样做并值得吗?还是在JWT内部存储用户信息和组,角色,权限足够安全?

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

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

再次尝试尝试简化流程:

在此处输入图片说明

Mat*_* Ke 9

下图显示了针对Web应用的带有第三方IDP(例如Google)的OpenID Connect身份验证流程。(不适用于您托管自己的IDP的体系结构。)

我不建议重用IDP中的ID或访问令牌来访问自己的API(两个令牌都只能在身份验证时使用。)相反,您应该实现OAuth2授权服务器,该服务器为您的API颁发访问令牌。(在这种情况下,您有两个OAuth2流。一个用于身份验证,另一个用于授权。)

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

OpenID Connect身份验证流程

1:您的Web应用已从应用服务器加载。

2a:用户单击您的登录按钮,您的Web应用程序将构建授权URL并将其打开。(请参阅:授权请求

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

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

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

4b:您的授权服务器将构建IDP授权URL并通过重定向对其进行响应。(请参阅:身份验证请求

5a:打开了IDP授权URL。

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

6:登录页面是从IDP加载的。

7a:用户填写其凭据,然后单击登录按钮。

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

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

(为简单起见,这里忽略了批准步骤。)

8b:IDP创建一个授权,并以重定向到您的授权服务器的回调URL的方式进行响应。(请参阅:身份验证响应

9a:打开回调URL。

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

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

(10b:如果ID令牌中没有某些所需的声明,您的授权服务器将调用IDP的用户信息端点。)

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

12a:授权URL再次打开。

(为简单起见,这里忽略了批准步骤。)

12b / + 13a / b:您的授权服务器创建/获取授权(创建访问令牌),并通过重定向到您的Web应用程序的回调URL进行响应。(请参阅:访问令牌响应

14a:打开回调URL。

14b:您的Web应用程序从回调URL中提取访问令牌。

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

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

  • 谢谢-这将非常有帮助!不仅对我,而且对其他许多人。我猜这种在内部交换令牌交换令牌的内部发生的令牌交换意味着将为客户端的每个API调用创建JWT,对吗?这意味着在进入网关的每个请求中,还将从数据库中查询17(get_user_roles)。这样,尽管从性能的角度来看并不是最有效的,但就安全性和灵活性而言,我认为也是最好的-因此控制权保留在网关/ auth服务中。 (2认同)
  • 是的,将为每个API调用创建JWT。(您可以将缓存添加到API网关以提高性能。可以容忍稍旧(约10秒)的用户详细信息。) (2认同)