跳到主要内容

认识

2025年01月08日
柏拉文
越努力,越幸运

一、认识


Web 应用中实现跨不同顶级域的 SSO(单点登录) 方案,通常会面临浏览器同源策略(Same-Origin Policy)的限制。在同顶级域下,通过设置 Cookiedomain 属性可以轻松实现跨子域的认证。然而,在不同顶级域(例如 example.comexample.org)下,由于浏览器的安全策略,不同的顶级域不能直接共享 Cookie, 直接通过 Cookie 来实现跨域认证变得困难。

OIDC 是建立在 OAuth 2.0 协议之上的,但与 OAuth 2.0 主要聚焦于授权不同,OIDC 关注的是用户身份认证。OIDC 允许客户端通过 ID Token 来验证用户的身份,同时还可以使用 Access Token 来访问授权的资源。

  • OAuth 2.0: 是一个授权框架,允许第三方应用在不暴露用户凭证的情况下,访问用户在某些资源服务器上的数据。

  • OpenID Connect (OIDC): 基于 OAuth 2.0 的身份验证协议,OIDC 不仅关注授权,还包括认证功能,允许客户端应用确认用户的身份。

二、工作流


基于 OIDC SSO 架构为:

  1. 身份提供者 (IdP Identity Provider): 比如 idp.example.com,负责用户的身份认证、颁发令牌 Access TokenRefresh TokenID Token, 提供统一认证服务, 并维护全局会话, 并将 Token 信息写入到 IdP 域的 Cookie

  2. 服务提供者 (SP Service Provider): app.example.comportal.example.orgccc.example.xyz 等多个 Web 应用, 受信任的第三方应用, 依赖于 IdP 来进行身份验证。用户通过 IdP 的令牌验证身份后,SP 创建局部会话, 并将 Token 信息写入到 SP 域的 Cookie

基于 OIDC SSO 工作流为:

1. 用户登录认证: 用户访问 SP1app.example.com 时,SP1 会检查是否已有有效的 Access Token 存储在 Cookie 中。如果没有有效的 Access TokenSP1 会将用户重定向到 IdP 进行认证。SP1 重定向到 IdP 授权端点时, 附带必要的查询参数,如 client_idredirect_uriscope(如 openid profile email)、response_type(如 code)等。用户在 IdP 提供的登录页面上输入用户名和密码(或通过其他身份认证方式),IdP 会验证用户身份并生成 授权码(Authorization Code。用户认证成功后, IdP 会创建全局会话, 并将 Token 信息写入到 IdP 域的 Cookie 中。然后 IdP 会将用户重定向回 SP1redirect_uri,并在 URL 中附带一个 授权码(codeSP1 将会使用授权码通过 POST 请求向 IdP 请求 Access TokenID TokenSP1 验证 ID Token 有效性通过令牌验证身份后,创建局部会话, 并将 Token 信息写入到 SP 域的 Cookie 中, 最后返回 SP1 页面。

  • IdP 使用 Cookie 标记全局会话: 并将对应 Token 存储到 IdP 域的 Cookie 中,并设置 HttpOnlySecureSameSite=None

  • SP 使用 ID Token 验证并管理用户的局部会话: 并将对应 Token 存储到 IdP 域的 Cookie 中,并设置 HttpOnlySecureSameSite=None

  • ID Token: 在 OIDC 中,ID Token 是一个 JWT,它包含了用户的身份信息。ID TokenOIDC 的核心组件,用于提供关于用户身份的声明。在我们的 IdP 实现中,生成 ID Token 是必要的,我们可以通过在 JWT 中添加一些关于用户的基本信息(如 subnameemail 等)来构建 ID TokenID Token 由身份提供者(IdP)签发,SP 可以使用这个 Token 来识别用户,并在后续的 API 调用中进行认证。

  • Access Token: Access Token 是一个 JWT 或其他格式的 Token,它用于代表授权的用户访问受保护的资源。Access Token 通常较短期有效,且其作用范围通常局限于授权的服务端 API

  • Refresh Token: Refresh Token 是一个长期有效的 Token,用于获取新的 Access Token,无需用户重新认证。通过刷新 Access Token,可以维持用户会话,避免频繁要求用户登录。

  • 授权码交换: 授权码 (Authorization Code) 是用来获取 Access TokenID TokenRefresh Token 的。首先,用户将被重定向到 IdP 进行认证,然后 IdP 会返回一个授权码给 SPSP 拿到授权码后,再通过后台向 IdP 申请 Access TokenID TokenRefresh Token,并通过这些 Token 来访问用户的身份信息。

  • Session 管理: OIDC 中的会话管理是另一个重要的部分,IdP 需要确保用户的会话在有效期内。通常会话信息需要存储在 Redis 或数据库中,确保在不同应用之间能共享会话信息。

2. 用户后续请求验证: SP1 在用户发起 API 请求时, 会自动携带局部 Access Token。当 Access Token 过期时,SP1 可以通过存储的 Refresh TokenIdP 请求新的 Access TokenID Token。这一步骤对用户来说是无感知的,无需重新登录。

3. 跨服务提供者的认证: 用户访问 SP2(portal.example.org)SP3(ccc.example.xyz) 时,SP2SP3 会检查用户是否已经认证。未认证时,会将用户重定向到 IdP 进行认证。IdP 会检查用户的全局会话,确认用户已经认证, 如果用户会话仍然有效, 会直接生成新的授权码, 省略了用户登录的流程。然后 SP2SP3 获取授权码之后, 通过授权码获取相关 Token 信息, 验证 ID Token, 创建局部会话, 并将 Token 信息写入到 SP 域的 Cookie 中, 最后返回 SP2SP3 页面。

用户在 SP1SP2 之间的认证是通过 IdP 中的全局会话来完成的。SP1SP2 都依赖于 IdP 维护的用户认证信息、全局会话,但它们分别维护各自的局部会话。这种跨域、跨应用的认证机制即实现了 SSO(Single Sign-On),用户只需要在第一次登录时输入用户名和密码,后续的访问请求都会自动完成认证,无需再次登录。

用户登录成功之后,会与 IdPIdentity Provider,身份提供者) 及各个 SPService Provider,服务提供者) 建立会话,用户与 IdPIdentity Provider,身份提供者) 建立的会话称为全局会话,用户与各个 SPService Provider,服务提供者) 建立的会话称为局部会话,局部会话建立之后,用户访问子系统受保护资源将不再通过 IdPIdentity Provider,身份提供者),全局会话与局部会话有如下约束关系

  • 局部会话存在,全局会话一定存在

  • 全局会话存在,局部会话不一定存在

  • 全局会话销毁,局部会话必须销毁