JSON Web Token,常被简称为 JWT,如今几乎是 Web 认证的标配。但围绕它的误解也不少,其中最常见的一个,是以为它"加密"了里面的数据。事实恰恰相反:JWT 默认并不隐藏任何内容,任何人拿到令牌都能读出里面的信息。它真正提供的,是一种可验证的可信性——服务器能确认这个令牌确实是自己签发、且没被篡改。理解这个区别,是用好 JWT 的关键。
令牌的三段结构
一个 JWT 由三段用点号分隔的部分组成:头部、载荷和签名。头部说明使用了哪种签名算法;载荷承载实际的声明,比如用户 ID、过期时间和权限;签名则是用来证明前两段没被改动的那道防线。三段各自用 Base64URL 编码,拼成一个可以放进 HTTP 头或链接的紧凑字符串。
正因为前两段只是编码而非加密,任何人都能把它们解码回原始 JSON。这不是缺陷,而是设计——令牌的安全性不依赖于内容保密,而依赖于签名。
编码不等于加密
这是最值得反复强调的一点:Base64URL 只是把内容变成便于传输的文本,它不提供任何保密性。把一段 JWT 的载荷贴进任何解码工具,里面的声明立刻一览无余。因此,绝不能把密码、密钥或任何敏感信息放进 JWT 的载荷里。
如果你确实需要让声明保密,那需要的是加密令牌(如 JWE),而不是普通的签名 JWT。把"看起来像乱码"当成"被保护了",是一个危险的误判。
签名才是信任的来源
JWT 的全部价值,几乎都集中在签名上。服务器用一个只有自己知道的密钥,对头部和载荷计算出签名。当令牌再次回到服务器时,服务器用同样的密钥重新计算签名,并与令牌里的签名比对。两者一致,就说明内容自签发以来没被动过;不一致,令牌就该被拒绝。
这意味着即便攻击者能读到载荷、甚至能修改它,他也无法在不知道密钥的情况下伪造出匹配的签名。可信性来自"无法伪造签名",而不是"无法读取内容"。
无状态是它的核心卖点
传统会话需要服务器把会话数据存在某处,每次请求都去查一遍。JWT 则把必要信息直接装进令牌本身,并用签名保证可信,于是服务器无需在自己这边保存任何会话状态,只要验证签名就能信任声明。
这种无状态特性,让 JWT 在分布式系统和多服务架构里特别受欢迎——任何持有密钥的服务都能独立验证令牌,不必共享一个集中的会话存储。
声明与过期时间
载荷里的声明可以是标准的,也可以是自定义的。标准声明包括签发者、受众、签发时间和过期时间等。其中过期时间尤为重要:它限定了令牌的有效期,让一个被泄露的令牌不至于永远有效。
验证令牌时,服务器不仅要核对签名,还要检查过期时间和其他相关声明。一个签名有效但已过期的令牌,同样应当被拒绝。完整的验证是签名校验和声明校验的结合。
无状态带来的撤销难题
无状态是优点,但也带来一个代价:令牌一旦签发,在过期之前很难"作废"。因为服务器并不保存令牌列表,所以无法简单地把某个令牌标记为失效。如果用户登出或令牌被盗,传统会话可以立即销毁,而 JWT 默认做不到。
应对之道通常是把有效期设得较短,再配合刷新令牌机制,或者额外维护一个撤销名单——但后者其实又把一部分状态加了回来。这正是选用 JWT 时必须正视的权衡。
把它放在合适的位置
JWT 是一件好工具,前提是用对地方。它擅长在无状态、分布式的场景里传递可验证的身份和权限信息;它不擅长保护机密内容,也不擅长需要即时撤销的场景。看清它"用签名保证可信、但不隐藏内容"的本质,你就能既享受它的无状态便利,又不至于把不该放的东西放进去。
归根结底,JWT 回答的问题是"这条声明能不能信",而不是"这条声明能不能被别人看到"。把这两个问题分清楚,你对 JWT 的使用就会安全得多。