VoociiBlog
HomeBlogAI TrendingPortfolioBooksLinksToolsAbout


Voocii© 2026. Built with Next.js & tRPC.
GitHubXEmailRSS

什么是OpenID Connect(OIDC)

Authrick-hayekrick-hayek2025年3月5日

之前我写过一个介绍OAuth+PKCE的文章,里面说到的用户登录,就是OIDC的方式。

OpenID Connect(OIDC)是建立在 OAuth 2.0 之上的身份认证协议。OAuth 解决“应用能访问哪些资源”,OIDC 进一步解决“当前用户是谁,以及如何验证这个身份”。

官网:https://openid.net/developers/how-connect-works/

OIDC 核心关系图

一、先用一句话理解 OIDC

假设用户正在访问一个第三方应用:

  • 应用希望知道用户是谁;
  • 用户不希望把密码交给这个应用;
  • 身份提供商(Identity Provider,IdP)负责登录、MFA 和身份校验;
  • 应用只希望拿到可信的身份结果。

OIDC 就是这套“委托登录”的标准化协议:用户在 IdP 完成认证,IdP 向客户端返回一个经过签名的 ID Token,客户端据此确认用户身份。

二、OIDC 的基础概念

1. 参与角色

角色含义示例
End-User需要登录的用户Alice
Client / Relying Party(RP)请求用户登录的应用企业后台、移动 App
OpenID Provider(OP)负责认证并签发令牌的服务Keycloak、Auth0、Microsoft Entra ID
Resource Server保存受保护 API 资源的服务用户中心 API
Authorization ServerOAuth 角色中的授权服务器,OIDC 中通常与 OP 合并IdP 的授权端点和令牌端点

一个 IdP 往往同时扮演 OP、Authorization Server 和部分 Resource Server 的角色,但协议概念上仍应区分它们。

2. 关键术语

  • Authentication(认证):确认“你是谁”。
  • Authorization(授权):确认“你能做什么”。
  • Scope:客户端请求的权限和身份信息范围,例如 openid profile email。
  • Claims:关于用户或令牌的声明,例如 sub、iss、aud、email。
  • ID Token:面向客户端的身份凭证,通常是 JWT。
  • Access Token:访问 API 的授权凭证,面向 Resource Server。
  • Refresh Token:用于换取新 Access Token 的长期凭证。
  • Discovery:通过标准元数据地址自动发现 IdP 端点和能力。
  • JWKS:IdP 发布的公钥集合,客户端用它验证 JWT 签名。

三、为什么引入 OIDC

OAuth 2.0 最初解决的是授权问题。客户端可以拿着 Access Token 调用 API,但仅凭 OAuth 规范,客户端并没有一个统一、可靠的方式回答:

这个 Token 对应的用户是谁?这个登录结果是不是由指定的身份提供商签发?

早期应用常见的做法是:拿 Access Token 调用某个用户信息 API,再自行约定返回字段。这会带来:

  1. 不同平台的用户信息接口和字段不一致;
  2. Access Token 的受众通常是 API,而不是客户端;
  3. 客户端难以统一验证签发者、受众、时间和签名;
  4. 把“登录”和“访问 API”混在了一起;
  5. 不同厂商各自实现,跨平台 SSO 成本较高。

OIDC 在 OAuth 2.0 上增加了标准化身份层:

  • 使用 openid scope 明确表示“这是一次 OIDC 认证请求”;
  • 使用 ID Token 传递身份声明;
  • 规定标准 Claims 和验证规则;
  • 定义 Discovery、UserInfo、JWKS 等互操作机制。

四、OAuth 2.0 和 OIDC 的区别

最简洁的对比是:

对比项OAuth 2.0OIDC
主要目标授权:允许客户端访问资源认证:确认用户身份,并可同时授权
核心问题“应用可以调用哪些 API?”“用户是谁?”
主要凭证Access TokenID Token + Access Token
Token 面向对象Resource ServerID Token 面向 Client,Access Token 面向 API
用户信息没有统一标准Claims、UserInfo 标准化
是否必须 JWTOAuth 不要求ID Token 必须是 JWT 格式
典型 scoperead:ordersopenid profile email
验证重点Token 是否能访问 APIiss、sub、aud、exp、签名、nonce 等

一个重要误区

不要把“拿到一个 OAuth Access Token”直接等价为“完成了 OIDC 登录”。

如果授权请求没有包含 openid scope,通常就是普通 OAuth 授权;即使返回了 JWT,也不能只因为它长得像 JWT 就把它当成 ID Token。Token 的用途由协议上下文、签发方、受众和验证规则共同决定。

五、OIDC 的核心原理

1. ID Token 是什么

ID Token 是 OP 发给客户端的身份声明,常见格式是 JWT,由三部分组成:

base64url(header).base64url(payload).base64url(signature)

一个简化的 Payload 示例:

{
  "iss": "https://id.example.com",
  "sub": "00u123456789",
  "aud": "web-client-123",
  "exp": 1720003600,
  "iat": 1720000000,
  "nonce": "n-0S6_WzA2Mj",
  "auth_time": 1720000000,
  "name": "Alice",
  "email": "[email protected]",
  "email_verified": true
}

2. 必须验证哪些内容

客户端不能只 Base64 解码 Payload 就信任用户身份。至少应验证:

  1. 签名:使用 OP 的 JWKS 公钥验证 JWT 签名;
  2. iss:必须等于客户端配置的 issuer;
  3. aud:必须包含当前客户端的 client_id;
  4. exp:不能已经过期;
  5. iat / auth_time:检查时间合理性和认证新鲜度;
  6. nonce:必须与客户端发起请求时保存的 nonce 一致,防止重放;
  7. azp:在特定多受众场景下验证 authorized party。

3. sub 才是稳定用户标识

应用不应使用 email 作为用户主键。OIDC 中推荐使用:

issuer + sub

这是一个在指定 issuer 下稳定且不可变的用户标识。不同 issuer 即使 email 相同,也不应默认视为同一个用户。

六、OIDC 的标准工作流程

生产环境最常用的是 Authorization Code Flow + PKCE。它适合 Web 应用、SPA 和移动端,是当前推荐的通用模式。

sequenceDiagram
  autonumber
  participant U as 用户
  participant C as Client / RP
  participant OP as OpenID Provider
  participant API as Resource Server

  U->>C: 访问需要登录的页面
  C->>C: 生成 state、nonce、PKCE code_verifier
  C->>OP: GET /authorize?response_type=code&scope=openid...
  OP->>U: 展示登录、同意和 MFA 页面
  U->>OP: 完成认证
  OP-->>C: 302 redirect_uri?code=...&state=...
  C->>C: 校验 state
  C->>OP: POST /token(code + code_verifier)
  OP-->>C: access_token + id_token + refresh_token
  C->>C: 验证 ID Token 签名和 Claims
  C->>API: Authorization: Bearer access_token
  API-->>C: 返回受保护资源

为什么需要 state、nonce 和 PKCE

  • state:绑定请求与回调,主要防御 CSRF;
  • nonce:绑定认证请求与 ID Token,主要防止 ID Token 重放;
  • PKCE:使用 code_verifier 证明换取 code 的客户端就是发起授权请求的客户端,降低授权码被截获后的风险。

七、逐个解析请求与响应

1. Discovery 请求

客户端可以从以下元数据地址开始:

GET /.well-known/openid-configuration HTTP/1.1
Host: id.example.com

典型响应:

{
  "issuer": "https://id.example.com",
  "authorization_endpoint": "https://id.example.com/oauth2/authorize",
  "token_endpoint": "https://id.example.com/oauth2/token",
  "userinfo_endpoint": "https://id.example.com/oauth2/userinfo",
  "jwks_uri": "https://id.example.com/.well-known/jwks.json",
  "response_types_supported": ["code"],
  "subject_types_supported": ["public"],
  "id_token_signing_alg_values_supported": ["RS256"],
  "scopes_supported": ["openid", "profile", "email"]
}

客户端应校验返回的 issuer,并避免把未经验证的 Discovery 地址直接当成可信配置。

2. Authorization Request

GET /oauth2/authorize?
  client_id=web-client-123&
  response_type=code&
  redirect_uri=https%3A%2F%2Fapp.example.com%2Fcallback&
  scope=openid%20profile%20email&
  state=af0ifjsldkj&
  nonce=n-0S6_WzA2Mj&
  code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM&
  code_challenge_method=S256 HTTP/1.1
Host: id.example.com

关键参数:

参数作用
client_id标识客户端
response_type=code使用授权码模式
redirect_uri认证完成后的回调地址,必须与注册值匹配
scope=openid声明这是 OIDC 请求
state防 CSRF 的随机值
nonce绑定本次认证与 ID Token
code_challengePKCE 的挑战值
code_challenge_method=S256指定挑战算法

3. Authorization Response

成功时,OP 通常重定向到:

HTTP/1.1 302 Found
Location: https://app.example.com/callback?
  code=SplxlOBeZQQYbYS6WxSbIA&
  state=af0ifjsldkj

客户端必须先比较回调中的 state 与本地保存值,再使用 code。授权码是短期、一次性的中间凭证,不是 Access Token,也不是 ID Token。

失败时可能返回:

error=access_denied
error_description=The+user+denied+the+request
state=af0ifjsldkj

即使是错误响应,也应验证 state 后再处理。

4. Token Request

服务端 Web 应用常通过后端调用 Token Endpoint:

POST /oauth2/token HTTP/1.1
Host: id.example.com
Content-Type: application/x-www-form-urlencoded
Authorization: Basic base64(client_id:client_secret)

grant_type=authorization_code&
code=SplxlOBeZQQYbYS6WxSbIA&
redirect_uri=https%3A%2F%2Fapp.example.com%2Fcallback&
code_verifier=dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk

公共客户端(例如 SPA、移动 App)通常不能安全保存 client_secret,因此依赖 PKCE,而不是把 secret 当作主要安全边界。

5. Token Response

{
  "access_token": "eyJhbGciOiJSUzI1NiIs...",
  "token_type": "Bearer",
  "expires_in": 3600,
  "id_token": "eyJhbGciOiJSUzI1NiIs...",
  "refresh_token": "def50200...",
  "scope": "openid profile email"
}

安全处理原则:

  • id_token 用于客户端建立登录会话,并按 OIDC 规则验证;
  • access_token 只发给它对应的 API,不能拿来代替 ID Token;
  • Refresh Token 应加密保存,并尽量使用轮换机制;
  • 不要把 Token 写入日志、URL、错误页面或前端埋点。

6. UserInfo Request / Response

当客户端需要更多用户 Claims 时,可以调用 UserInfo Endpoint:

GET /oauth2/userinfo HTTP/1.1
Host: id.example.com
Authorization: Bearer eyJhbGciOiJSUzI1NiIs...

响应示例:

{
  "sub": "00u123456789",
  "name": "Alice",
  "preferred_username": "alice",
  "email": "[email protected]",
  "email_verified": true
}

UserInfo 响应中的 sub 必须与 ID Token 的 sub 一致,否则客户端不能把它们视为同一用户。

八、实际案例:企业后台接入公司 IdP

假设有一个订单管理后台 orders.example.com,公司使用 login.company.com 作为统一身份中心。

注册客户端

client_id: orders-web
redirect_uri: https://orders.example.com/oidc/callback
post_logout_redirect_uri: https://orders.example.com/
allowed_scopes: openid profile email orders.read orders.write

登录路径

  1. 用户访问 /orders,应用发现没有本地会话;
  2. 应用生成 state、nonce 和 PKCE 参数;
  3. 浏览器跳转到公司 IdP;
  4. 用户完成密码、MFA 或企业单点登录;
  5. IdP 回调授权码;
  6. 后端校验 state,用授权码换取 Token;
  7. 后端验证 ID Token,并用 iss + sub 查找或创建本地用户;
  8. 应用建立自己的安全会话;
  9. 访问订单 API 时,使用 Access Token 或服务端会话代表用户访问。

为什么应用仍然需要自己的会话

OIDC Token 是协议凭证,而应用会话是业务系统自己的登录状态。服务端可以将验证后的身份映射到本地用户、角色、租户和权限,再通过 HttpOnly、Secure、SameSite Cookie 建立会话。这样可以避免把长期 Token 暴露给浏览器业务代码。

九、常见应用场景

1. 企业 SSO

多个内部系统统一跳转到一个 IdP,用户只需登录一次。应用通过 iss + sub 识别身份,并从 Claims 或企业目录映射部门、角色和租户。

2. 社交登录

“使用 Google / Microsoft / GitHub 登录”通常使用 OIDC 或带有 OIDC 能力的 OAuth 方案。应用不接触用户密码,只接收标准化身份声明。

3. SPA 与移动 App

使用 Authorization Code Flow + PKCE。客户端不保存 client secret,授权码通过 code verifier 保护。

4. 多租户 SaaS

每个企业租户可以绑定不同 IdP。应用通过 Discovery 动态获取端点,并严格校验 issuer,避免错误地接受来自其他租户的 Token。

5. API 网关和服务平台

登录由 OIDC 负责,API 权限由 OAuth scope、角色和策略系统负责。二者结合,可以把“用户身份”和“接口授权”分层管理。

十、安全清单

  • 使用 Authorization Code Flow + PKCE;
  • 全程 HTTPS,严格配置回调地址;
  • 每次登录生成不可预测的 state 和 nonce;
  • 验证 JWT 签名、iss、aud、exp、nonce;
  • 不把 Access Token 当作 ID Token;
  • 不把 email 当作不可变主键;
  • 限制 scope 和 audience,遵循最小权限;
  • 对 Refresh Token 进行安全存储和轮换;
  • 处理 JWKS 密钥轮换和缓存;
  • 防止 Token 出现在日志、Referer 和浏览器历史中;
  • 对 Logout、会话过期和账号解绑设计明确策略。

十一、总结

OIDC 并不是 OAuth 的替代品,而是 OAuth 之上的身份层:

flowchart TB
  OIDC[OpenID Connect:认证用户是谁]
  OAuth[OAuth 2.0:授权访问哪些资源]
  HTTP[HTTP / TLS:传输与保密]
  OIDC --> OAuth --> HTTP
  ID[ID Token:给 Client 的身份声明] --> OIDC
  AT[Access Token:给 API 的访问凭证] --> OAuth

记住三句话:

  1. openid scope 表示请求 OIDC 身份认证;
  2. ID Token 给客户端确认用户身份,Access Token 给 API 授权;
  3. 生产环境应优先使用 Authorization Code + PKCE,并完整验证 Token。

理解这三层关系,就能看懂大多数 SSO、社交登录、企业身份联邦和现代 Web 登录系统。

Comments (0)

No comments yet. Be the first!

On this page
  • 一、先用一句话理解 OIDC
  • 二、OIDC 的基础概念
  • 三、为什么引入 OIDC
  • 四、OAuth 2.0 和 OIDC 的区别
  • 五、OIDC 的核心原理
  • 六、OIDC 的标准工作流程
  • 七、逐个解析请求与响应
  • 八、实际案例:企业后台接入公司 IdP
  • 九、常见应用场景
  • 十、安全清单
  • 十一、总结