OIDC 提供商搭建:Keycloak、Okta、Azure AD

聊到 OIDC 的实现,第一步就是选一个靠谱的授权服务器。你可以自己搭,也可以用云服务。我做了几年安全集成,发现很多团队在这步就卡住了——选型不对,后面全是坑。这一节我把三个主流选项的实战经验掏出来说说。

三个选型,先看一张对比表

先别急着搭,把差异搞清楚。

维度 Keycloak Okta Azure AD
部署方式 自托管(Docker / WildFly) SaaS / 云托管 Azure 云服务
成本 开源免费,基础设施自付 按用户/月费,开发版免费 随 Azure 订阅,含在身份许可
特性 完整 OIDC / OAuth2,社交登录,用户联盟 成熟的企业级策略引擎,自适应 MFA 与 Microsoft 365 / Entra ID 强绑定,条件访问
适用场景 需要完全控制,离线环境,二次开发 追求快速集成,无运维团队 企业已有微软生态,需要和 SharePoint / Teams 联动

我个人习惯先回答两个问题:要自托管吗? 以及 现有的身份源在哪里? 下面这张图可以直接帮你做决策。

OIDC 提供商选型决策树 开始选型 需要完全自托管? Keycloak 开源免费 · 完全可控 已用微软生态? Azure AD 企业身份 · 条件访问 Okta 开发友好 · 快速集成

Keycloak 实战:从容器部署到 OIDC 客户端

如果你选 Keycloak,最省事的方式是用官方 Docker 镜像。我在一个离线项目中试过直接解压 WildFly 运行,嗯,能跑,但维护起来真的累。后来全换 Docker Compose 了。

快速部署(生产级)

# docker-compose.yml
version: '3'
services:
  keycloak:
    image: quay.io/keycloak/keycloak:22.0
    command: start --optimized
    environment:
      KC_HOSTNAME: auth.mydomain.com
      KC_HTTPS_CERTIFICATE_FILE: /etc/certs/tls.crt
      KC_HTTPS_CERTIFICATE_KEY_FILE: /etc/certs/tls.key
      KEYCLOAK_ADMIN: admin
      KEYCLOAK_ADMIN_PASSWORD: ${ADMIN_PW}
      KC_DB: postgres
      KC_DB_URL_HOST: postgres
      KC_DB_URL_DATABASE: keycloak
      KC_DB_USERNAME: keycloak
      KC_DB_PASSWORD: ${DB_PW}
    volumes:
      - ./certs:/etc/certs
    ports:
      - 443:8443

部署完后,进 Admin Console 创建 Realm → 创建 Client(选择 openid-connect)→ 配置 Valid Redirect URIs。我习惯把 Access Type 设成 confidential,然后打开 Service Accounts Roles,这样后端服务可以用 client_credentials 拿 。

不要偷懒用 public 模式暴露前端客户端。如果你有 SPA,记得用 PKCE flow,否则你的 client_secret 会在浏览器里裸奔。

Okta 配置:三步搞定 OIDC 应用

Okta 的 SaaS 体验确实好。你先注册一个 Developer 账号(永久免费,但只限 1000 用户)。然后:

  1. 创建 Application:点 Applications → Add Application → Web 或 Single-Page App。如果你做的是服务端渲染的 Web 应用,选 Web 并用 Authorization Code + PKCE。
  2. 配置 Grant Types:默认只有 Authorization Code。我建议同时勾上 Refresh ,并设置一个合理的过期窗口(默认 90 天太长了,我改成 7 天)。
  3. 获取 Issuer 和 Client ID:Okta 会给你一个 https://{你的租户}.okta.com/oauth2/default 作为 Issuer。把这个地址配到你的 OIDC 客户端 SDK 里。

我记得有一次遇到一个奇怪的问题:Okta 的登录页面一直跳转回来,报 invalid redirect_uri。查了半天发现是 URI 里多了一个尾部斜杠。Okta 对 redirect_uri 的匹配是严格的。

Okta 的默认 CORS 策略很严格,如果你用 SPA + 自定义域名,务必在 API 设置里显式添加允许的来源。否则用户在第三方浏览器上登录时会直接白屏。

Azure AD:企业生态里的 OIDC 实战

Azure AD(现在叫 Entra ID)是我工作中接触最多的。如果你的公司已经用了 Office 365,那选这个基本不用纠结。但配置时有几个点容易翻车。

配置项 Keycloak / Okta Azure AD
注册应用入口 Admin Console → Clients Azure Portal → 应用注册
平台配置 列出所有 redirect_uri 需要选择平台类型(Web / SPA / 移动)
权限声明 默认包含 profile / email / openid 需要手动添加 Microsoft Graph 权限
证书与机密 支持 client_secret 和 证书 推荐使用证书(client_credential)

我在为一个内部工具集成 Azure AD 时,发现 里居然没有 email 声明。这是因为 Azure AD 默认不会返回 email,除非你在 manifest 里把 optionalClaims 加上。代码示例:

// 在 Azure 应用注册的 manifest 中添加
"optionalClaims": {
  "idToken": [
    {
      "name": "email",
      "source": "user"
    }
  ]
}

另外,Azure AD 的 条件访问策略 是它的杀手锏。比如你可以要求从非公司 IP 登录时需要 MFA。但要注意,条件访问策略会影响 获取流程——如果你的后端用 client_credentials 获取 ,那个 里面不会携带用户上下文,条件访问不会触发。我之前踩过这个坑,导致一个后台服务在非公司网络下死活拿不到 。

避坑指南(亲测)

最后说三个我踩得最深的坑,你碰到也能快速定位:

  • 端点响应慢——有一次 Keycloak 的 端点要 5 秒才响应。排查发现数据库连接池满了,因为默认连接数只设了 20。生产环境建议调高到 100 以上,并且加连接池监控。
  • Okta 的 Rate Limit——免费版每个端点每分钟 600 次请求。我的 CI 流水线并发跑 10 个 Job 同时刷新 ,直接把 IP 给封了。解决方法是给每个 Job 配一个独立的 Okta 应用,或者在 Job 之间加一个 缓存层。
  • Azure AD 的 JWT 签名——Azure AD 偶尔会轮换签名密钥(默认 60 天)。如果你在客户端里硬编码了 JWKS URI 的缓存,一旦密钥轮换,你的 验证就会全部失败。我写了一个定时任务每 12 小时重新拉取 /.well-known/openid-configuration。

搭建 OIDC 提供商本身不复杂,真正的工程挑战在于 密钥轮换 和 高可用。下一节我们会专门讲 secret 轮换如何与这些提供商联动。不过那是下一章的事了,先把提供商跑起来再说。

小结:选型看自托管需求、现有生态、运维能力。Keycloak 适合极客控制狂,Okta 适合快速验证,Azure AD 适合微软全家桶。不管你选哪个,先把 的获取和验证跑通,再考虑密钥轮换。