Skip to content
Published on

用 Keycloak 搭建 SSO — 从 Realm、Client、登录流程到 2026 年新功能

分享
Authors

引言 — 登录为什么不该自己造

假设你有 20 个内部应用。如果给每一个都分别实现注册、密码重置、2FA、社交登录、会话管理,安全漏洞就会变成 20 倍。SSO(Single Sign-On)的想法很简单 — 设立一台专门负责登录这件事的服务器,让所有应用都把"这个人是谁"这个问题交给它来回答。用户只需登录一次,就能使用全部 20 个应用,而这些应用从头到尾都不会碰到密码。

Keycloak 是这台身份服务器的开源标准实现(目前是 CNCF 孵化项目,也是 Red Hat build of Keycloak 的上游)。它同时支持 OIDC 和 SAML,并内置社交登录代理、LDAP 集成、2FA、WebAuthn。本文先用 四个核心概念 理解 Keycloak,再一步步走完登录流程,最后整理部署陷阱和 2026 年的新功能。如果对认证流程本身还不熟悉,先玩一下认证流程可视化工具,读起来会轻松很多。

第一部分 — 用四个核心概念看 Keycloak

Keycloak 的管理控制台菜单有几十个,但骨架只有四个。

概念                角色                                    比喻
─────────────────  ──────────────────────────────────────  ──────────────────
Realm              完全隔离的租户(自有用户/配置)            一个"王国"
Client              被委托认证的应用                          王国"发放通行证的对象"
Role / Group        权限与用户的组合                          "职位"与"部门"
Identity Provider    外部登录来源(谷歌、上游 OIDC/SAML)        "承认另一个王国的护照"
  • Realm(域) 是隔离边界。一个 Realm 内有自己的用户、客户端、角色、登录设置,与其他 Realm 完全分离。管理员账号所在的 master Realm 只应该用来管理 Keycloak 本身,实际服务的标准做法是另开一个 Realm。如果你的第一反应是"想按客户分隔用户"就用 Realm — 后面会看到,很多时候 Organizations 才是更好的答案。
  • Client(客户端) 是"被委托认证的应用"。跑在浏览器里的 SPA 藏不住密钥,所以是 public 客户端(必须加 PKCE);后端服务器能守住密钥,所以是 confidential 客户端。每个客户端都有自己的协议(OIDC 或 SAML)、允许的 redirect URI、令牌有效期等设置。
  • Role / Group 是权限模型。Role 是"职位"(admin、editor),Group 是"部门"(用户组合 + 共同的角色和属性)。也可以通过把一个角色嵌套进另一个角色(composite role)来搭建层级。原则是:把 角色当作应用理解的权限,把 组当作组织人的方式,分开来想。
  • Identity Provider(IdP) 是"外部登录来源"。它代理谷歌、GitHub 社交登录,或公司上游的 OIDC/SAML IdP,让 Keycloak 站在前面充当中介。LDAP、Kerberos 不是以 IdP 的形式接入,而是通过 User Federation(直接读取用户存储)接入。

第二部分 — 一步步走完 OIDC 登录流程

最常用的流程是 Authorization Code + PKCE。让我们跟着一个 SPA 把登录委托给 Keycloak 的过程走一遍。

① 用户点击应用的"登录"
   → 应用把浏览器重定向到 Keycloak 的 authorize 端点
     (包含 client_id、redirect_uri、scope=openid、code_challenge)

② Keycloak 显示登录页面 → 用户完成认证(密码 + OTP 等)
   ※ 如果已经有 Keycloak 会话,则跳过这个页面 = SSO 的核心

③ Keycloak 重定向回 redirect_uri,并附上一次性的 authorization code

④ 应用后端(或 SPA)用 code + code_verifier 到 token 端点交换
   → 拿到 access_token(JWT)+ id_token + refresh_token

⑤ 应用把 access_token 放进 API 调用的 Authorization 头发出
   API 只用 Keycloak 的公钥(JWKS)验证签名 — 不会每次请求都去问 Keycloak

这里有三件事是关键。第一,PKCE(code_challenge/code_verifier)能让被窃取的 authorization code 失效 — 对 public 客户端来说是必需的。第二,②中"有会话就跳过"正是 SSO 的核心。当第二个应用委托登录时,Keycloak 已经通过浏览器 Cookie 认出了用户,不显示登录页面就立刻签发 code。第三,⑤中 API 不会每次请求都调用 Keycloak — 因为 access_token 是签名过的 JWT,只需要 用公钥做本地验证 就够了。正是这种无状态性,让 SSO 能够扩展。

三种令牌各自的角色也不要混淆:

id_token       "谁登录了" — 用户信息。由应用(客户端)消费
access_token   "能做什么" — 权限。由 API(资源服务器)验证
refresh_token  "重新获取的权利" — 用于 access_token 过期时续期,需安全保管

会话和令牌有效期的调优,是 Realm 设置里的核心杠杆。标准做法是让 access_token 短(几分钟),refresh_token 长(直到 SSO 会话有效期),短 access_token + 本地签名验证的组合,能同时给你"就算被盗也很快失效"和"验证速度快"这两样东西。

第三部分 — 部署:Quarkus 两阶段模型与生产环境陷阱

从 Keycloak 17 开始,发行版就基于 Quarkus(旧的 WildFly/JBoss 发行版已被移除)。这里初次接触的人最容易踩的坑,是 build/start 两阶段模型

# ① 构建阶段(augmentation):把数据库厂商、启用的功能"烤"进去
#    — 这一步比较慢,但不需要经常做
bin/kc.sh build --db=postgres --features=organizations,token-exchange

# ② 启动阶段:只传运行时设置(主机名、数据库连接、TLS)
bin/kc.sh start --optimized \
  --hostname=https://auth.example.com \
  --db-url=jdbc:postgresql://db/keycloak \
  --db-username=keycloak --db-password="$KC_DB_PASSWORD"

# 本地开发用的便捷命令,把两个阶段合二为一
bin/kc.sh start-dev

--optimized 标志的意思是"已经 build 过了,start 时不要再重新构建"。在容器镜像里,标准做法是把构建阶段放进 Dockerfile 烤进镜像,运行时只跑 start --optimized

生产环境中最常踩的三颗地雷:

  • hostname v2 — 从 Keycloak 26 开始,hostname 配置完全变了。现在 --hostname 要传的不是主机名片段,而是 完整 URL(https://auth.example.com);要把管理控制台暴露在另一个地址,需要单独传 --hostname-admin。如果在反向代理后面,还要加上 --proxy-headers=xforwarded。配置错了,redirect URI 就会泄露成内部地址,或者陷入登录循环。
  • 生产模式强制要求 TLSstart(非 dev 模式)如果没有正确配置 HTTPS、hostname、数据库,就起不来。如果代理终止了 TLS,必须用 --proxy-headers 告诉 Keycloak,它才能正确识别自己的外部地址。
  • 数据库就是全部状态 — Keycloak 服务器本身几乎无状态,用户、会话、配置全部存在外部数据库里(推荐 Postgres)。这让水平扩展很容易,但数据库的备份和迁移也就等于身份数据的备份。

在 Kubernetes 上,Keycloak Operator 通过 KeycloakKeycloakRealmImport CRD 把这一切都用声明式的方式包起来。如果需要先培养 Kubernetes 的手感,可以先在Kubernetes 练习场里熟悉一下 CRD 和 Operator 模式。

第四部分 — 2026 年的 Keycloak:26.x 新功能

截至本文撰写时,最新版本是 26.6.3(内部使用 Quarkus 3.33.2)。在 26 系列转正的功能里,实战影响较大的有四个:

  • Organizations — 在 26.0 正式发布,是"多租户的正确答案"。此前"按客户隔离"要么开多个 Realm,要么用组来模拟,两种做法都有痛点(Realm 太重,组的隔离太弱)。Organizations 让你 在同一个 Realm 内,为每个组织维护成员、域名、专属 IdP,以组织上下文登录时,该成员关系会自动写入 OIDC 令牌和 SAML 断言。在 26.x 的演进过程中,它还具备了按组织隔离的组层级,以及基于外部声明的自动分组分配(IdP 映射器)。记住这句话:"Realm 是彼此完全陌生的系统,Organizations 是同一系统内的多个客户。"
  • JWT Authorization Grant (RFC 7523) — 用外部签名的 JWT 断言获取 Keycloak 访问令牌的标准方式。作为替代旧有"外部→内部令牌交换"的推荐路径,它让服务间信任可以建立在标准协议之上。
  • Workflows — Realm 管理任务(例如清理非活跃用户、针对特定事件的后续处理)的自动化功能已经转正。以前靠自定义扩展或外部 crontab 完成的工作,现在成了服务器自带的功能。
  • 零停机补丁发布 — 支持在补丁版本之间做滚动更新,可以不停止集群就应用安全补丁。对于"绝对不能宕机"的身份基础设施来说,这是一大进步。

令牌交换(token exchange)目前仍有边界 — 现在只支持 OIDC/OAuth 交换,基于 SAML 的客户端和 IdP 交换还是未来的工作。因此建立外部信任时,上面的 JWT Authorization Grant 才是更标准的答案。

第五部分 — 什么时候该用,什么时候该重新考虑

Keycloak 大放异彩的场景:在本地部署或混合环境里需要 数据主权,需要覆盖广泛的协议(OIDC + SAML),或者要在没有许可费用的前提下大规模扩展。与托管 SaaS(Auth0、Okta、Entra ID)之间的分岔路口,通常在于"你愿意承担运维负担,还是愿意承担成本和被锁定" — Keycloak 让你完全掌控数据和定制化,代价是数据库、升级、可用性都要自己扛。

需要重新考虑的地方:身份服务器是 单点故障。它一旦挂掉,20 个应用会同时无法登录。所以多实例 + 外部会话存储(Infinispan/DB)+ 零停机补丁不是可选项,而是必需品。而且如果漏掉了令牌与会话有效期、redirect URI 白名单、强制 PKCE 这些安全基本功,SSO 反而会把攻击面集中到一处。建议在认证与安全实验室里亲手跑一遍 OAuth、OIDC、JWT,把这种直觉打磨扎实。

结语

归根结底,Keycloak 是一套把登录"汇聚到一处"的基础设施。用 Realm(隔离)、Client(应用)、Role/Group(权限)、IdP(外部集成)四个概念搭起骨架,用 Authorization Code + PKCE 理解流程,用 build/start 两阶段 + hostname v2 避开部署陷阱,用 Organizations 解决多租户问题 — 20 个应用的登录就会收敛到一扇被好好守护的门。开源同时又老老实实遵循标准,正是这个项目十多年来被喜爱的原因。

参考资料