3. 客户端凭证与用户凭证的区别

在 OAuth 2.0 的四种授权模式中,客户端凭证(Client Credentials Grant) 与 密码模式(Password Grant)、授权码模式(Authorization Code Grant) 是生产环境中最常被对比的三种流程。它们的核心差异在于:凭证的持有者是谁,以及令牌代表的是“应用身份”还是“用户身份”。

核心原则:客户端凭证模式是唯一一种没有用户参与的授权模式。它适用于服务器到服务器(M2M)的通信,令牌代表应用自身的权限;而密码模式和授权码模式都代表最终用户的授权。

3.1 三种模式的凭证持有者对比

对比维度 客户端凭证模式 密码模式 授权码模式
凭证持有者 客户端应用(Client) 最终用户(Resource Owner) 最终用户(Resource Owner)
是否需用户交互 否(完全后台) 是(用户输入用户名/密码) 是(用户通过浏览器授权)
令牌代表身份 应用身份(Application) 用户身份(User) 用户身份(User)
典型使用场景 微服务间调用、定时任务、后台批处理 第一方原生应用(已不推荐) Web 应用、移动端、第三方登录
刷新令牌支持 通常不支持 支持 支持
安全风险等级 低(凭证仅存于服务端) 高(暴露用户密码) 中(依赖 redirect_uri 安全)

3.2 流程差异详解

3.2.1 客户端凭证模式(Client Credentials)

客户端使用自己的 client_id 和 client_secret 直接向授权服务器请求令牌,不涉及用户凭证。授权服务器验证客户端身份后,直接颁发 access_token。

  • 请求示例:
POST /oauth/token HTTP/1.1
Host: auth.example.com
Content-Type: application/x-www-form-urlencoded

grant_type=client_credentials
&client_id=your_client_id
&client_secret=your_client_secret
&scope=read write
  • 令牌用途:令牌中通常包含 client_id 和权限范围,不包含用户标识(如 sub 为用户 ID)。
  • 后端权限矩阵映射:权限直接绑定到客户端应用,而非用户。例如:client:report-service 拥有 read:orders 权限。

3.2.2 密码模式(Password Grant)

用户将自己的用户名和密码直接交给客户端,由客户端代为向授权服务器请求令牌。此模式要求客户端绝对可信(通常是第一方应用)。

  • 请求示例:
POST /oauth/token HTTP/1.1
Host: auth.example.com
Content-Type: application/x-www-form-urlencoded

grant_type=password
&username=user@example.com
&password=user_password
&client_id=your_client_id
&client_secret=your_client_secret
&scope=openid profile
  • 令牌用途:令牌代表用户身份,包含 sub(用户 ID)、preferred_username 等用户信息。
  • 安全警告:OAuth 2.0 Security BCP(最佳实践)已不推荐使用密码模式,因为它会暴露用户密码,且不支持多因素认证。

3.2.3 授权码模式(Authorization Code Grant)

用户通过浏览器访问授权服务器的授权页面,登录并授权后,授权服务器通过回调 URL 返回一个临时授权码(code),客户端再用此 code 换取 access_token。整个过程用户密码仅在授权服务器页面输入,客户端无法获取用户密码。

  • 流程步骤:
  • 用户访问客户端 → 客户端重定向到授权服务器。
  • 用户登录并授权 → 授权服务器重定向回客户端,附带 ?code=xxx。
  • 客户端使用 code + client_secret 换取 access_token。
POST /oauth/token HTTP/1.1
Host: auth.example.com
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code
&code=authorization_code_value
&redirect_uri=https://client.example.com/callback
&client_id=your_client_id
&client_secret=your_client_secret
  • 令牌用途:令牌代表用户身份,且支持 PKCE(Proof Key for Code Exchange) 增强安全性。
最佳实践提示:对于需要代表用户访问资源的场景,始终优先选择授权码模式 + PKCE。密码模式仅应在遗留系统迁移或绝对信任的第一方应用中使用,并尽快迁移。

3.3 三种模式的架构对比图

下图展示了三种模式在“凭证流向”和“令牌代表身份”上的本质区别:

三种 OAuth 2.0 模式的凭证与令牌流向对比 三种授权模式的凭证与令牌流向对比 客户端凭证模式 客户端应用 client_id + client_secret 授权服务器 access_token(应用身份) 资源服务器 无用户参与 令牌代表:应用 密码模式 最终用户 用户名 + 密码 客户端应用 转发凭证请求令牌 授权服务器 用户密码暴露给客户端 令牌代表:用户 授权码模式 最终用户 在授权页面登录 授权服务器 返回授权码(code) 客户端应用 用户密码不暴露给客户端 令牌代表:用户

3.4 后端权限矩阵的映射差异

在接口安全配置中,三种模式对应的权限矩阵设计思路完全不同:

模式 权限主体 权限矩阵示例 接口鉴权方式
客户端凭证 客户端 ID(client_id) client:report-service → [read:orders, read:users] 验证 token 中的 client_id 和 scope
密码模式 用户 ID(sub) user:alice → [read:orders, write:orders] 验证 token 中的 sub 和角色/权限声明
授权码模式 用户 ID(sub)+ 客户端 ID user:alice + client:web-app → [read:orders] 验证 token 中的 sub、aud(受众)和 scope
安全设计要点:在客户端凭证模式下,永远不要将用户级别的敏感数据(如个人订单、用户资料)暴露给仅持有应用令牌的客户端。应通过 scope 严格限制客户端可访问的资源范围,并在资源服务器端实施客户端白名单校验。

3.5 选择指南

在实际系统设计中,可依据以下决策树快速选择:

  • 是否有最终用户参与?
    • 否 → 客户端凭证模式(M2M 通信)
    • 是 → 继续判断
  • 客户端是否绝对可信(第一方应用)?
    • 是且无法使用授权码 → 密码模式(仅限遗留系统)
    • 否 → 授权码模式 + PKCE(推荐)
总结:客户端凭证模式是“应用身份”的代表,密码模式和授权码模式是“用户身份”的代表。理解这一本质区别,是设计后端权限矩阵和接口安全策略的基础。在微服务架构中,客户端凭证模式常用于服务间认证,而授权码模式则用于用户前端交互。