简体中文
  • oidc
  • oauth
  • authentication
  • authorization
  • jwt

将 OIDC 服务器集成到项目中的完整指南

了解将 OIDC (OpenID Connect)服务器集成到项目中的最佳实践,并了解组件在舞台上如何相互交互。

Gao
Gao
Founder

您可能会遇到需要集中身份验证和授权系统的情况,也就是身份访问管理(IAM)或身份提供者(IdP)。有时,人们会在名称后附加一个单词来指代业务,如客户 IAM 和员工 IAM。

让我们暂时放下这些花哨的名称。对 IAM 的需求可能是因为您的应用程序正在增长,或者计划从一开始就将繁重的工作委托给供应商。不论如何,你正在到达需要在项目中引入身份系统的阶段。

考虑到 OAuth 2.0 的流行,OpenID Connect (OIDC)对许多开发者来说是一个自然的选择。由于 OIDC 是一个构建在 OAuth 2.0 之上的身份验证层,开始使用 OIDC 时您可能会感到熟悉。让我们开始吧!

什么是 OIDC 服务器,以及我为什么要集成 OIDC 服务器?

OIDC 服务器,或身份提供者,是一个管理用户身份验证和授权的集中系统。正如我们在为什么您需要一个多应用业务的集中身份系统中讨论的那样,集中身份系统有很多好处。

假设您的项目从一个简单的 Web 应用程序开始,并且它内置了身份验证:

随着项目的增长,您需要引入一个移动版本:

如果用户需要为每个应用程序创建一个帐户,那么体验会很糟糕。由于您从 Web 应用程序开始,将移动应用程序与 Web 应用程序通信进行身份验证:

现在,引入了新的 API 服务。由于这是为付费用户提供的服务,您需要确保用户经过身份验证并被授权访问该服务。为此,您可以通过 Web 应用程序代理该服务:

或者,使用一些令牌技术验证用户,并通过 Web 应用程序在服务中验证令牌。因此移动应用程序可以直接使用该服务:

事情变得混乱。所以你决定将身份验证和授权逻辑拆分为一个独立的服务:

重构过程可能是痛苦的。随着项目增加更多应用程序和服务,您可能会注意到其复杂性成倍增加。更糟糕的是,您可能需要维护多种身份验证方法,如密码登录、社交登录、SAML 等。

这就是为什么在计划扩展项目时,我们最好从一开始就引入身份提供者。

集成 OIDC 服务器的最佳实践

寻找 OIDC 提供者

市场上有许多 OIDC 提供者。您可以根据自己的需求和偏好选择一个。只要提供者符合 OIDC 标准,它将在您的项目中扮演相同的角色。

OIDC 中的“subject”、“client”和“audience”是什么意思?

简化这个概念,我们可以认为 subject 是通过 client 请求访问 audience 的实体。

让我们看看一些典型场景:

1. 用户点击网页应用上的登录按钮

在传统和服务端渲染的 Web 应用程序中,前端和后端是耦合的。假设 web 应用程序同时服务于前端和后端:

  • 主体(Subject):用户
  • 听众(Audience):OIDC 服务器
  • 客户(Client):Web 应用程序

audience 是 OIDC 服务器这一点看起来可能有些违反直觉。事实上,这是实现终端用户 SSO (单点登录) 体验的关键。让我们来看一个简化的授权码流程的序列图:

code 是一个一次性代码,可以用来交换各种令牌,如访问令牌、ID 令牌和刷新令牌。如果您目前不理解这些令牌,这没关系。随着我们前进,您将更好地理解它们。

在上述情况下,当用户切换到另一个应用程序时不需要再次登录,因为用户(主体)已经通过 OIDC 服务器(听众)进行身份验证。

2. 用户使用单页应用程序

在单页应用程序(或移动应用程序)中,前端和后端是分离的。假设后端是一个 API 服务:

  • 主体(Subject):用户
  • 听众(Audience):API 服务
  • 客户端(Client):单页应用程序(SPA)

使用授权码流程的简化序列图:

由于 API 服务是非交互式的,SPA 需要使用 API 服务作为听众的访问令牌 (即令牌中的 aud)。

为什么 OIDC 服务器仍然是听众?

技术上,你可以从聆众列表中移除 OIDC 服务器。由于在大多数情况下,您将需要 OIDC 服务器的用户信息(这需要 OIDC 服务器成为听众),因此在涉及用户交互时,最好始终将 OIDC 服务器包括在听众列表中。

等等,你是说可以在授权请求中有多个听众?

正是如此!请记住,OIDC 是建立在 OAuth 2.0 之上的,在授权请求中利用 RFC 8707: OAuth 2.0 的资源指标 来指定多个听众,这是可能的。它需要授予以及 OIDC 服务器的支持。Logto 原生支持这个特性。

3. 一台机器与另一台机器的通信

假设你有一项服务 A 需要调用服务 B:

  • 主体(Subject):服务 A
  • 听众(Audience):服务 B
  • 客户端(Client):服务 A

使用客户端凭证授予的简化序列图:

当服务 B 需要调用服务 A 时,只需交换角色即可。

回顾

  • 主体(Subject):可以是用户、服务或任何需要访问听众的实体。
  • 客户端(Client):可以是 Web 应用、移动应用或任何发起请求或代表主体行事的实体。
  • 听众(Audience):可以是服务、API 或任何为主体提供访问的实体。

什么是访问令牌、ID 令牌和刷新令牌?

在使用 OIDC 时,您可能会遇到三种类型的令牌:

  • 访问令牌:用于访问听众。它可以是 JWT (JSON Web Token)或不透明令牌(通常是随机字符串)。
  • ID 令牌:一个特定于 OIDC 的令牌,包含用户信息。它始终是 JWT 。客户可以解码令牌以获取用户信息。
  • 刷新令牌:用于在访问令牌或 ID 令牌过期时获取一组新令牌。

有关这些令牌的详细说明,您可以参考了解 OIDC 协议中的刷新令牌、访问令牌和 ID 令牌

在上述方案 1 和 2 中,术语 授权请求 指的是通过特定 授予 来获取一组令牌的请求。

当一切顺利时,在 "使用 code 交换令牌" 步骤中会返回一组令牌。该集合中的可用令牌取决于多个因素,特别是授权请求中的 scope 参数。为简化起见,我们假设所有令牌都在集合中返回。一旦访问令牌过期,客户端可以使用刷新令牌在用户无交互的情况下获取新的令牌集。

对于方案 3 来说更简单,因为客户端凭据授予只返回访问令牌。

如何在 OIDC 中处理多个听众?

您可能注意到一次只返回一个访问令牌。那客户需要访问多个听众时该如何处理?

有两种常见解决方案:

在代码交换请求中指定 resource

当客户端交换代码以获取令牌时,可以在请求中指定一个 resource 参数。如果适用,OIDC 服务器将返回一个该指定听众的访问令牌。

这是一个非规范性示例:

然后 OIDC 服务器将返回该 API_SERVICE 听众的访问令牌,如果适用的话。

使用刷新令牌获取新访问令牌

使用 RFC 8707,客户端甚至可以使用 resource 参数多次指定多个听众。现在,如果客户端中可用刷新令牌,客户端可以在刷新令牌时在 resource 参数中指定听众。

这是一个非规范性示例:

其效果与之前的解决方案相同。同时,在未来的令牌请求中,其他已授予的听众仍然可用。

客户端凭据授予

您还可以在客户端凭据授予中使用 resource 参数来指定听众。对于此授予来说没有多个听众的问题,因为您可以始终通过简单地发送另一个令牌请求来请求不同听众的新访问令牌。

保护您的 API 服务

方案 2 中的 "API 服务" 和方案 3 中的 "服务 B" 有一个共同点:它们需要验证访问令牌以确定请求是否被授权。根据访问令牌的格式,验证过程可能会有所不同。

  • 不透明令牌:API 服务需要调用 OIDC 服务器来验证令牌。OIDC 服务器通常会提供一个内省端点用于此目的。
  • JWT:API 服务可以通过检查签名和令牌中的声明来本地验证令牌。OIDC 服务器通常会提供一个JSON Web 密钥集合(JWKS)端点jwks_uri)供 API 服务获取公钥以验证签名。

如果您是 JWT 新手,可以参考什么是 JSON Web Token (JWT)?。事实上,通常您不需要手动验证签名和断言声明,因为有很多库可以为您完成这些工作,如适用于 Node.js 和 Web 浏览器的 jose

断言声明

除了验证 JWT 签名之外,API 服务还需始终检查令牌中的声明:

  • iss:令牌的颁发者。它应与 OIDC 服务器的颁发者 URL 匹配。
  • aud:令牌的听众。它应与 API 服务的听众值匹配(通常是有效的 URI)。
  • exp:令牌的到期时间。如果令牌已过期,API 服务应拒绝该令牌。
  • scope:令牌的范围(权限)。API 服务应检查所需范围是否存在于令牌中。

其他声明,例如 sub(主体)和 iat(签发时间),在某些情况下也很重要。如果您有额外的安全措施,请相应检查声明。

授权

仍然未解答的问题是:我们如何确定某个 scope(即权限)是否可以授予给某个主体?

这个问题引出了一个全新的授权世界,这超出了本文的范围。简而言之,有一些常见的方法,如 RBAC (基于角色的访问控制)和 ABAC (基于属性的访问控制)。这里有一些资源可以帮助您入门:

结束语

将 OIDC 服务器引入项目是一个重要的步骤。它可以显著提高项目的安全性和可扩展性。同时,您可能需要花一些时间来理解概念以及组件之间的交互。

选择一个适合您需求和偏好的 OIDC 提供者可以显著减少集成过程的复杂性,因为提供者通常会提供完整的解决方案,包括 OIDC 服务器、授权机制、SDK 以及您未来可能需要的企业功能。

我希望这份指南能帮助你理解集成 OIDC 服务器的基础知识。如果您正在寻找一个开始的地方,我会自私地推荐 Logto,我们的开发者身份基础设施。