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 三种模式的架构对比图
下图展示了三种模式在“凭证流向”和“令牌代表身份”上的本质区别:
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(推荐)
总结:客户端凭证模式是“应用身份”的代表,密码模式和授权码模式是“用户身份”的代表。理解这一本质区别,是设计后端权限矩阵和接口安全策略的基础。在微服务架构中,客户端凭证模式常用于服务间认证,而授权码模式则用于用户前端交互。