几个月前,我正在实施一个 OpenID Connect 服务器来管理我们数百个内部应用程序的访问。 从我们自己的发展来看,在较小的规模上很方便,我们已经转向了一个普遍接受的标准。 通过中央服务接入大大简化了单调的操作,降低了实施授权的成本,让您可以找到许多现成的解决方案,而无需在开发新解决方案时绞尽脑汁。 在本文中,我将讨论这一转变以及我们设法填补的障碍。
很久以前……这一切是如何开始的
几年前,当内部应用太多需要人工控制时,我们写了一个应用来控制公司内部的访问。 这是一个简单的 Rails 应用程序,连接到包含员工信息的数据库,其中配置了对各种功能的访问。 同时,我们提出了第一个单点登录,它是基于客户端和授权服务器端对令牌的验证,令牌以带有多个参数的加密形式传输,并在授权服务器上进行验证。 这不是最方便的选择,因为每个内部应用程序都必须描述相当多的逻辑层,并且员工数据库与授权服务器完全同步。
一段时间后,我们决定简化集中授权的任务。 SSO 被转移到平衡器。 在 OpenResty 的帮助下,Lua 中添加了一个模板,用于检查令牌,知道请求将发送到哪个应用程序,并可以检查那里是否有访问权限。 这种方法大大简化了控制对内部应用程序访问的任务——在每个应用程序的代码中,不再需要描述额外的逻辑。 结果,我们对外关闭了流量,应用程序本身对授权一无所知。
然而,还有一个问题没有解决。 那些需要员工信息的应用程序呢? 可以为授权服务编写一个 API,但是您必须为每个这样的应用程序添加额外的逻辑。 此外,我们希望在我们的内部授权服务器上摆脱对我们自己编写的应用程序之一的依赖,该应用程序面向未来转换为 OpenSource。 我们改天再谈。 这两个问题的解决方案是 OAuth。
符合共同标准
OAuth 是一个可以理解、被普遍接受的授权标准,但由于只有它的功能是不够的,他们立即开始考虑 OpenID Connect(OIDC)。 OIDC 本身是开放式身份验证标准的第三个实现,它已流入 OAuth 2.0 协议(一种开放式授权协议)之上的附加组件。 该解决方案解决了缺少最终用户数据的问题,并且还可以更改授权提供者。
但是,我们没有选择特定的提供商,而是决定为我们现有的授权服务器添加与 OIDC 的集成。 支持这一决定的是 OIDC 在最终用户授权方面非常灵活的事实。 因此,可以在您当前的授权服务器上实施 OIDC 支持。
我们实现自己的 OIDC 服务器的方式
1)将数据转换为所需的形式
要集成 OIDC,需要将当前的用户数据转换成标准可以理解的形式。 在 OIDC 中,这称为声明。 声明本质上是用户数据库中的最终字段(姓名、电子邮件、电话等)。 存在
这组标志组合成以下子集 - 范围。 在授权期间,请求访问的不是特定品牌,而是范围,即使不需要范围内的某些品牌。
2) 实施必要的拨款
OIDC 集成的下一部分是授权类型的选择和实施,即所谓的授权。 所选应用程序与授权服务器之间的进一步交互场景将取决于所选授权。 下图显示了选择正确授权的示例方案。
对于我们的第一个应用程序,我们使用了最常见的授权,即授权码。 它与其他的不同之处在于它是一个三步,即正在进行额外的测试。 首先,用户请求授权许可,收到一个token——Authorization Code,然后用这个token,就像用一张旅行票一样,请求一个access token。 此授权脚本的所有主要交互都基于应用程序和授权服务器之间的重定向。 您可以阅读有关此赠款的更多信息
OAuth秉承授权后获得的access token应该是临时的,最好平均每10分钟变化一次的理念。 Authorization Code grant是通过redirects进行的三步验证,每10分钟转这么一步,坦白说,这不是最赏心悦目的工作。 为了解决这个问题,还有一个grant——Refresh Token,我们国内也用过。 这里的一切都更容易。 在另一个授权的验证过程中,除了主要的访问令牌之外,还会颁发另一个 - 刷新令牌,它只能使用一次,并且它的生命周期通常要长得多。 有了这个 Refresh Token,当主访问令牌的 TTL(生存时间)结束时,对新访问令牌的请求将来到另一个授权的端点。 使用过的刷新令牌立即重置为零。 此检查分为两步,可以在后台执行,用户察觉不到。
3)设置自定义数据输出格式
实施选定的授权后,授权工作,值得一提的是获取有关最终用户的数据。 OIDC 为此有一个单独的端点,您可以在其中使用当前的访问令牌请求用户数据以及它是否是最新的。 而如果用户的数据没有那么频繁的变化,需要多次跟随当前的,可以来JWT token这样的解决方案。 该标准也支持这些令牌。 JWT token本身由三部分组成:header(关于token的信息)、payload(任何必要的数据)和signature(签名,token是由服务器签名的,你可以稍后查看其签名的来源)。
在 OIDC 实现中,JWT 令牌称为 id_token。 它可以与普通访问令牌一起请求,剩下的就是验证签名。 授权服务器为此有一个单独的端点,带有一堆格式的公钥
例如在谷歌上:
{
"issuer": "https://accounts.google.com",
"authorization_endpoint": "https://accounts.google.com/o/oauth2/v2/auth",
"device_authorization_endpoint": "https://oauth2.googleapis.com/device/code",
"token_endpoint": "https://oauth2.googleapis.com/token",
"userinfo_endpoint": "https://openidconnect.googleapis.com/v1/userinfo",
"revocation_endpoint": "https://oauth2.googleapis.com/revoke",
"jwks_uri": "https://www.googleapis.com/oauth2/v3/certs",
"response_types_supported": [
"code",
"token",
"id_token",
"code token",
"code id_token",
"token id_token",
"code token id_token",
"none"
],
"subject_types_supported": [
"public"
],
"id_token_signing_alg_values_supported": [
"RS256"
],
"scopes_supported": [
"openid",
"email",
"profile"
],
"token_endpoint_auth_methods_supported": [
"client_secret_post",
"client_secret_basic"
],
"claims_supported": [
"aud",
"email",
"email_verified",
"exp",
"family_name",
"given_name",
"iat",
"iss",
"locale",
"name",
"picture",
"sub"
],
"code_challenge_methods_supported": [
"plain",
"S256"
],
"grant_types_supported": [
"authorization_code",
"refresh_token",
"urn:ietf:params:oauth:grant-type:device_code",
"urn:ietf:params:oauth:grant-type:jwt-bearer"
]
}
因此,使用 id_token,您可以将所有必要的标志转移到令牌的有效负载中,而不必每次都联系授权服务器来请求用户数据。 这种方法的缺点是来自服务器的用户数据的变化不会立即到来,而是伴随着一个新的访问令牌。
执行结果
因此,在应用程序端实现了我们自己的 OIDC 服务器并配置了与它的连接之后,我们就解决了用户信息传输的问题。
由于 OIDC 是一个开放标准,我们可以选择现有的提供者或服务器实现。 我们尝试了 Keycloak,事实证明它配置起来非常方便,在应用程序端设置和更改连接配置后,就可以使用了。 在应用程序方面,剩下的就是更改连接配置。
谈论现有的解决方案
在我们的组织内,作为第一个 OIDC 服务器,我们组装了自己的实现,并根据需要进行了补充。 在详细审查了其他现成的解决方案之后,我们可以说这是一个有争议的问题。 为了支持实施他们自己的服务器的决定,供应商方面担心缺乏必要的功能,以及存在一个旧系统,其中对某些服务有不同的自定义授权,而且很多有关员工的数据已经存储。 但是,在现成的实现中,有集成的便利。 例如,Keycloak有自己的用户管理系统,数据直接存储在里面,在那里你的用户超车并不难。 为此,Keycloak 有一个 API,可让您完全执行所有必要的传输操作。
在我看来,另一个经过认证的、有趣的实施示例是 Ory Hydra。 这很有趣,因为它由不同的组件组成。 要集成,您需要将您的用户管理服务链接到他们的授权服务并根据需要进行扩展。
Keycloak 和 Ory Hydra 并不是唯一的现成解决方案。 最好选择OpenID基金会认证的实现。 这些解决方案通常具有 OpenID 认证徽章。
如果您不想保留 OIDC 服务器,也不要忘记现有的付费提供商。 今天有很多不错的选择。
接下来是什么
在不久的将来,我们将以不同的方式关闭内部服务的流量。 我们计划将我们当前使用 OpenResty 的平衡器上的 SSO 转移到基于 OAuth 的代理。 这里已经有很多现成的解决方案,例如:
其他材料
来源: habr.com