为什么需要 Access Token 和 ID Token 两个令牌

为什么需要 Access Token 和 ID Token 两个令牌

一句话概括:Access Token 用于"授权"(Authorization),解决"能访问什么资源"的问题;ID Token 用于"认证"(Authentication),解决"这个用户是谁"的问题。 两者对应的是两个不同的协议目标、不同的消费方、不同的生命周期,混用会直接破坏安全模型。

一、背景:OAuth 2.0 与 OpenID Connect 的分工

OAuth 2.0 生来只做一件事------委托授权:用户把自己的资源访问权委托给第三方应用,应用拿着 Access Token 去调用资源服务器(API)。它并不关心"用户是谁",甚至可以在完全没有用户参与的情况下(如客户端凭据模式)签发 Access Token。

但在实际场景中,应用往往还想知道"登录的人到底是谁"。早期开发者把 OAuth 2.0 曲解为认证协议(用 Access Token 去调用用户信息接口),结果催生了大量"令牌混淆"类的安全漏洞。OpenID Connect(OIDC)正是为了解决这个问题,在 OAuth 2.0 之上加了一层身份认证协议,其核心产物就是 ID Token------一个签名的 JWT,直接向客户端应用声明"认证已发生,用户是谁"。

所以这两个 Token 的并存,本质上是授权(OAuth 2.0)与认证(OIDC)两条职责线的分离。

二、两者对比

维度 ID Token Access Token
所属协议 OpenID Connect(认证层) OAuth 2.0(授权层)
核心用途 向客户端证明"用户是谁、已完成认证" 允许客户端访问受保护的资源(API)
消费方 客户端应用本身 资源服务器
受众标识 客户端的 Client ID 目标 API 的标识
格式 固定为 JWT JWT 或不透明字符串
关键载荷 sub、iss、aud、auth_time、nonce、用户声明 scope、aud、exp、客户端标识等
典型操作 客户端本地验签、建立会话、渲染用户信息 随请求发给 API,由 API 校验 aud/scope/exp 后做授权决策
有效期/流转 通常较短,只在登录时消费,不向下游传递 会在客户端与多个 API 之间频繁传递,靠 Refresh Token 续期

三、ID Token:给客户端看的"身份证明"

ID Token 的定位是一份由身份提供方签名的 JWT,客户端拿到后本地验签即可确认三件事:令牌来自可信签发方、用户确实通过了认证、令牌没有被篡改。

它的正确用法仅限于客户端内部:

  • 建立本地会话 :解析 sub 确定用户身份,创建登录态;
  • 个性化展示 :直接读取 name、email 等声明渲染界面,省去再调一次 userinfo 接口的开销;
  • 防重放 :配合请求时传入的 nonce 声明,防止令牌被重放。
    特别要注意 aud 声明------它写的是客户端自己的 Client ID。这个设计意味着:ID Token 从协议层面就只发给客户端自己,不是给 API 用的凭据。

四、Access Token:给 API 看的"通行证"

Access Token 是一份委托授权凭据:它代表"某个用户(或客户端)授权了某个应用,以特定的 scope 访问某个资源"。

它的正确用法是随请求发送到资源服务器,由 API 侧完成校验:

  1. 验证签名与签发方;
  2. 核对 aud 是否指向本 API;
  3. 检查 scope 是否覆盖本次操作所需权限;
  4. 检查 exp 过期时间。
    OAuth 2.0 核心规范对 Access Token 的格式没有任何约束,它可以是 JWT 也可以是不透明字符串------因为 API 与授权服务器之间可以约定如何解析令牌,客户端只需要原样转发即可。这种不透明性还有个附带好处:客户端拿到的令牌即使泄露,攻击者也读不出内部信息,只有通过 introspection 才能解析。

五、为什么必须拆成两个:深层设计逻辑

1. 认证与授权的生命周期不同。 用户登录的频率远低于 API 调用的频率。ID Token 在登录时消费一次、用于建立会话后基本不再流转;Access Token 则需要在客户端与各 API 之间高频传递、过期后通过 Refresh Token 无感续期。若合二为一,要么身份信息跟着令牌被反复传播扩大泄露面,要么强制用户频繁重新登录。

2. 受众隔离是安全边界。 ID Token 的 aud 是客户端,Access Token 的 aud 是 API。每个令牌只在"预期接收方"那里被验证通过,出了这个圈子就无效。这种设计使得即使令牌被意外转发,另一端也会因受众不匹配而拒绝它。

3. 职责分离让各参与方只需最小信息。 API 做授权决策时通常只需要 scope 和用户标识,不需要姓名、邮箱这类身份敏感数据;客户端做认证时只需要身份声明,不需要权限细节。把两类信息装进不同令牌,本质上是最小权限原则 在协议层的体现------令牌中泄露的信息越少,泄露造成的损害越小。

4. 灵活性。 有些流程只要认证不要授权(如纯前端登录),有些只要授权不要用户身份(如后台服务的客户端凭据模式)。两种令牌独立签发,才能按需组合。

六、典型的误用及其风险

实践中最常见的错误是把两者当成"同一个东西的两种格式":

  • 拿 ID Token 去调 API :因为 ID Token 的 aud 是客户端而非 API,且没有机制把它绑定到客户端与 API 之间的通道上------攻击者一旦窃取 ID Token,就能直接拿它去冒充调用 API。正确做法是 API 只接受 Access Token,并严格校验其 aud 与 scope。
  • 在 API 里解析 ID Token 做授权决策:ID Token 表达的是"认证发生了",不表达"被授权了什么操作"。授权依据必须来自 Access Token 的 scope。
  • 把 Access Token 当用户信息来源 :Access Token 未必包含用户资料,获取用户资料应走 ID Token 或 userinfo 端点。
    一个正确的调用链可以概括为:应用读 ID Token 确认"谁登录了",再把 Access Token 发给 API 去获取"能做什么",两条线各走各的,永不交叉。

小结

双 Token 设计不是协议的历史包袱,而是刻意为之的架构选择:ID Token 服务于 OIDC 的认证目标、面向客户端;Access Token 服务于 OAuth 2.0 的授权目标、面向资源服务器。受众不同、载荷不同、生命周期不同、验证方不同------正是这些差异共同构成了现代身份体系中认证与授权的清晰边界。

相关推荐
逛逛GitHub2 小时前
3 个最近爆火的 Personal Agent ,在 GitHub 上有开源平替了。
github
小猪写代码2 小时前
GitHub 科普
github
高频因子挖掘机2 小时前
同一只股票前复权和不复权价格对不上?先检查这几个口径
后端·github·api
鱼宵2 小时前
Spring AI 流式输出:Flux + SSE 打字机,回答不再干等三秒
java·人工智能·spring·sse·springai·流式输出
shaibdoio2 小时前
AI主动获客的意图识别工程实现:从规则匹配到模型微调
java·开发语言·人工智能·长沙瞬维ai
for_ever_love__2 小时前
MySQL 事务隔离级别讲透:MVCC、幻读与四个级别怎么选
java·数据库·mysql·事务·mvcc·不可重复读·幻读
米多科技3 小时前
我做了一个 3D 虚拟世界基底,在上面再搭 3D 世界或元宇宙,事半功倍
javascript·人工智能·github
小小龙学IT3 小时前
Go 语言 gRPC(grpc-go)深度解析
rpc·架构·golang·go