1. 令牌基础认知:什么是访问令牌、令牌的组成结构(JWT/OAuth2)、令牌的生命周期

聊访问令牌之前,我先问个问题:你平时登录完一个网站,后端是怎么知道「你就是你」的?

说白了,就是靠一个令牌。你拿着它,系统就认你。我刚开始接触这块时,总觉得令牌就是个随机字符串,没什么技术含量。后来被线上事故狠狠教育了一次——令牌泄露导致数据被爬,才明白这东西有多关键。

1.1 什么是访问令牌

访问令牌(Access ),是客户端用来访问受保护资源的凭证。它像一把临时钥匙,告诉服务器:「我有权限,让我进去。」

我个人习惯把令牌比作酒店房卡。你办入住时前台给你一张卡,这张卡能开你的房门、能去健身房、能在餐厅挂账。但卡上有有效期,退房那天中午12点就失效了。访问令牌也是这个道理——它有时效、有范围、有权限。

核心要点:访问令牌是客户端与资源服务器之间的信任凭证。它不直接代表用户身份,而是代表一次授权的结果。

我在项目中遇到过不少团队把令牌当用户身份用,存数据库、打日志、传参数……结果令牌泄露后,攻击者拿着它就能冒充用户。记住:令牌是凭证,不是身份。

1.2 令牌的组成结构

现在主流的令牌格式有两种:透明令牌(Opaque )和结构化令牌(JWT)。

透明令牌就是一串随机字符,比如 a1b2c3d4e5f6...,资源服务器拿到后得去授权服务器验证。JWT 则不同,它自己就包含了所有信息。

1.2.1 JWT 结构

JWT 由三部分组成,用点号分隔:

header.payload.signature

举个例子:

eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0.
POstGetfAytaZS82wHcjoTyoqhMyxXiWdR7Nn7A29NSlQ

这三部分分别是:

部分 内容 编码方式
Header 令牌类型、签名算法 Base64URL
Payload 声明(Claims),如用户ID、角色、过期时间 Base64URL
Signature 对前两部分的签名,防止篡改 算法生成

Header 长这样:

{
  "alg": "RS256",
  "typ": "JWT"
}

Payload 里常见的声明:

  • sub(Subject):令牌主体,通常是用户ID
  • exp(Expiration Time):过期时间,Unix时间戳
  • iat(Issued At):签发时间
  • scope:权限范围
  • client_id:客户端标识

注意:Payload 只是 Base64URL 编码,不是加密!任何人拿到令牌都能解码看到里面的内容。千万不要把密码、信用卡号等敏感信息放进去。

我曾经见过一个系统,把用户手机号直接写在 JWT 的 Payload 里,日志还打了完整令牌……嗯,那周我帮他们改了三天代码。

1.2.2 OAuth2 中的令牌

OAuth2 是一个授权框架,它定义了四种获取令牌的方式(授权码模式、客户端凭证模式、密码模式、隐式模式)。但不管哪种模式,最终给客户端的都是一个 Access 。

OAuth2 本身不规定令牌格式,你可以用 JWT,也可以用透明令牌。我个人更推荐 JWT,因为资源服务器可以本地验证,不用每次去授权服务器查一次。

但 JWT 也有缺点——一旦签发,无法撤销。你想想看,如果用户改了密码,旧的 JWT 在过期前仍然有效。这就是为什么 OAuth2 里通常搭配 Refresh 一起用。

1.3 令牌的生命周期

令牌不是永久的,它有完整的生命周期。我习惯把它分成四个阶段:

  1. 签发:授权服务器验证用户身份后,生成令牌返回给客户端
  2. 传输:客户端在请求中携带令牌(通常放在 Authorization Header)
  3. 使用:资源服务器验证令牌有效性,处理请求
  4. 销毁:令牌过期、被撤销或用户主动登出

下面这张图能帮你理清整个流程:

访问令牌生命周期 用户认证 用户名+密码 签发令牌 生成JWT/透明令牌 传输令牌 Authorization Header 验证令牌 签名+过期+权限 令牌销毁 过期/撤销/登出 令牌生命周期从用户认证开始,到令牌销毁结束。每个环节都有安全风险,需要针对性防护。

1.3.1 签发阶段

签发时要注意两个关键点:过期时间和权限范围。

  • 过期时间:我建议 Access 的过期时间控制在 15-30 分钟。太长不安全,太短用户体验差。
  • 权限范围:只给最小必要权限。比如一个只读接口,就别给写权限。

小技巧:用 Refresh 实现长期登录。Access 短时效,Refresh 长时效(比如7天),过期了自动刷新。这样既安全又方便。

1.3.2 传输阶段

令牌传输必须走 HTTPS,这是底线。我见过有人为了省事,在 HTTP 里传令牌,结果被中间人抓包,整个系统沦陷。

传输方式推荐用 Authorization: Bearer <>,不要放 URL 参数里。URL 会被浏览器历史、服务器日志记录下来,等于把令牌到处扔。

1.3.3 使用阶段

资源服务器收到令牌后,要做三件事:

  1. 验证签名(JWT)或调用授权服务器验证(透明令牌)
  2. 检查是否过期
  3. 检查权限是否匹配

我曾经遇到一个案例:资源服务器只验证了签名,没检查过期时间。结果有人拿着一年前的令牌还能访问系统……修复方式很简单,加一行 exp 校验代码。

1.3.4 销毁阶段

令牌销毁有三种方式:

  • 自然过期:到达 exp 时间后自动失效
  • 主动撤销:用户登出或管理员强制下线,将令牌加入黑名单
  • 重新签发:用 Refresh 换新 Access ,旧令牌作废

重要提醒:JWT 一旦签发,服务端无法主动让它失效(除非维护黑名单)。所以如果你的业务需要即时撤销能力,建议用透明令牌或维护一个令牌黑名单缓存。

嗯,到这里令牌的基础认知就讲完了。记住三个核心:令牌是凭证不是身份、JWT 自包含但不可撤销、生命周期每个环节都要做安全防护。后面我们会深入每个环节的攻防细节。