JWT 是什么?从登录认证说起

举报
yd_232225224 发表于 2026/08/29 18:48:36 2026/08/29
【摘要】 jwt简介

在做 Web 开发时,登录认证几乎是绕不开的一部分。

用户输入账号密码登录之后,服务器总得想办法记住“这个请求是谁发过来的”。传统做法通常是 Session:用户登录成功后,服务器创建一份会话数据,同时给浏览器一个 Session ID。之后浏览器每次请求都带上这个 ID,服务器再根据 ID 找到对应的用户信息。

JWT(JSON Web Token)则提供了另一种思路:服务器不再依赖 Session ID 去查询一份会话数据,而是直接生成一个 Token,把一些必要的信息放进 Token 中,再交给客户端保存。

客户端之后访问接口时,只需要携带这个 Token,服务器验证 Token 合法之后,就可以知道当前请求对应的是哪个用户。

这也是 JWT 在前后端分离项目中非常常见的原因。

JWT 长什么样

一个典型的 JWT 大概是这样的:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
.
eyJzdWIiOiIxMDAxIiwibmFtZSI6IlRvbSIsImlhdCI6MTcxMDAwMDAwMH0
.
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

看起来很长,但实际上它只有三个部分:

Header.Payload.Signature

也就是:

头部.载荷.签名

三个部分之间使用 . 分隔。

JWT 的很多设计其实都围绕这三部分展开。

Header:告诉服务器这个 Token 怎么生成的

第一部分 Header 一般用来描述 JWT 本身,例如:

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

其中:

alg

表示签名使用的算法,例如 HS256、RS256。

而:

typ

一般表示当前 Token 的类型是 JWT。

这个 JSON 最后会经过 Base64URL 编码,变成 JWT 中的第一段。

需要注意的是,Base64 并不是加密。

也就是说,Header 里面的内容实际上是可以直接解码查看的。

Payload:真正保存数据的地方

JWT 第二部分叫 Payload,也就是载荷。

比如:

{
  "sub": "1001",
  "name": "Tom",
  "role": "admin",
  "iat": 1710000000,
  "exp": 1710003600
}

这里通常会保存一些与用户身份相关的信息。

例如:

sub

表示 Token 对应的主体,很多项目会在这里保存用户 ID。

iat 表示 Token 的签发时间,exp 表示 Token 的过期时间。

当然,也可以放一些自己的字段,比如:

{
  "userId": 1001,
  "username": "Tom",
  "role": "admin"
}

这样服务器收到 JWT 后,就可以直接读取用户 ID 和角色,而不一定需要每次都根据 Session ID 查询会话。

不过这里有一个很容易误解的地方:

Payload 不是加密的。

它和 Header 一样,本质上只是进行了 Base64URL 编码。

所以 JWT 中不应该保存密码、身份证号、银行卡信息之类的敏感数据。

只要拿到了 Token,任何人理论上都可以解码出 Payload。

Signature:JWT 真正重要的部分

如果 Header 和 Payload 都可以被解码,那是不是意味着用户可以直接修改 JWT?

例如把:

{
  "role": "user"
}

改成:

{
  "role": "admin"
}

单纯修改 Payload 确实很容易。

但问题在于,JWT 还有第三部分:Signature,也就是签名。

以 HS256 为例,签名的逻辑可以简单理解成:

HMACSHA256(
    Base64Url(Header) + "." + Base64Url(Payload),
    secret
)

这里的 secret 是服务器自己保存的密钥。

例如服务器生成 Token 时使用:

my-super-secret-key

攻击者虽然可以修改 Payload,却不知道服务器的密钥,因此无法生成正确的新签名。

服务器收到 JWT 后,会重新计算一次签名。

如果计算出来的结果和 JWT 中携带的 Signature 不一样,就说明这个 Token 被修改过了,服务器可以直接拒绝请求。

所以 JWT 的签名解决的并不是“别人看不到数据”,而是:

证明这份数据没有被篡改。

这两者是完全不同的概念。

JWT 登录流程

假设现在有一个登录接口:

POST /login

用户提交:

{
  "username": "tom",
  "password": "123456"
}

服务器首先检查用户名和密码。

如果验证成功,就生成一个 JWT:

eyJhbGciOiJIUzI1NiJ9.xxxxx.xxxxx

然后返回给客户端:

{
  "token": "eyJhbGciOiJIUzI1NiJ9.xxxxx.xxxxx"
}

客户端拿到 Token 后保存下来。

之后访问需要登录的接口,例如:

GET /api/user

客户端通常会在 HTTP 请求头中加入:

Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.xxxxx.xxxxx

服务器收到请求后,从 Authorization 中取出 JWT。

然后检查:

  • Token 的格式是否正确
  • 签名是否正确
  • Token 是否已经过期

如果验证全部通过,就认为这个 Token 是可信的。

之后便可以从 Payload 中取出用户 ID:

userId = 1001

再继续处理当前请求。

JWT 为什么适合前后端分离

传统 Session 模式下,真正的登录状态通常保存在服务器。

例如:

Session ID -> 用户信息

服务器收到请求之后,需要根据 Session ID 找到对应 Session。

如果系统只有一台服务器,这没有什么问题。

但如果以后变成:

客户端
   ↓
负载均衡
   ↓
服务器 A
服务器 B
服务器 C

问题就出现了。

用户登录时 Session 可能创建在服务器 A 上,而下一次请求却被负载均衡发送到了服务器 B。

服务器 B 并没有这份 Session。

当然,这个问题可以通过 Redis 共享 Session 等方式解决。

JWT 的思路则不同。

用户身份信息本身就在 Token 中:

客户端
   ↓
JWT
   ↓
任意服务器

只要所有服务器都拥有验证 JWT 所需要的密钥,就可以判断这个 Token 是否有效。

因此 JWT 天然比较适合分布式 API 和前后端分离场景。

JWT 并不是绝对的“无状态”

JWT 经常被描述为“无状态认证”。

从服务器不保存 Session 这个角度来说,这个说法没有问题。

但真正做项目的时候,事情往往没有那么简单。

比如一个 JWT 的有效期是 7 天:

{
  "exp": 1710604800
}

如果用户第二天主动退出登录怎么办?

客户端当然可以把 Token 删除。

但如果这个 Token 已经被别人复制了,那么它理论上仍然可以继续使用,直到 7 天后过期。

因为服务器只检查:

签名正确吗?

过期了吗?

两个答案都是正常的。

服务器并不知道这个 Token 已经“退出登录”。

如果想让某个 JWT 提前失效,就可能需要维护 Token 黑名单,例如放进 Redis:

token -> revoked

这时候系统又出现了服务器端状态。

因此,与其说 JWT 一定是无状态的,不如说:

JWT 允许我们实现无状态认证,但具体系统是否真的无状态,还是取决于业务设计。

Access Token 和 Refresh Token

实际项目中一般也不建议直接发一个有效期几个月的 JWT。

因为 Token 的有效期越长,泄露之后造成的风险越大。

比较常见的方式是同时使用:

Access Token
Refresh Token

Access Token 的有效期比较短。

例如:

15 分钟
30 分钟
1 小时

平时调用业务接口都使用 Access Token。

而 Refresh Token 的有效期比较长,比如:

7 天
30 天

当 Access Token 过期之后,客户端拿 Refresh Token 请求服务器:

POST /refresh

服务器验证 Refresh Token 后,重新签发新的 Access Token。

这样即使 Access Token 被泄露,攻击者能够利用它的时间也比较有限。

而 Refresh Token 因为权限更敏感,通常需要更加谨慎地保存和管理。

JWT 放在哪里比较合适

浏览器项目里经常可以看到两种做法。

一种是:

localStorage

然后 JavaScript 主动读取 Token,并放进请求头:

Authorization: Bearer xxx

这种方式实现起来非常简单,但如果网站出现 XSS 漏洞,恶意 JavaScript 就有机会读取 localStorage 中的 Token。

另一种方式是把 Token 放进 Cookie,并设置:

HttpOnly
Secure
SameSite

其中 HttpOnly 可以阻止普通 JavaScript 直接读取 Cookie。

不过 Cookie 又涉及 CSRF 等问题,因此实际项目中不能简单地认为某一种方式绝对安全。

Token 放在哪里,本质上还是要结合系统架构和安全模型来设计。

JWT 和 Session 到底选哪个

JWT 出现之后,有一段时间经常能看到一种说法:

JWT 比 Session 更先进。

其实并不是这样。

JWT 和 Session 解决的是类似的问题,但设计思路不同。

Session 更像是:

客户端保存一个钥匙编号
服务器保存真正的数据

JWT 更像是:

客户端携带一张服务器签名的身份证明
服务器负责检查这张证明是不是真的

Session 的优势是登录状态更容易控制。

例如管理员想强制某个用户退出,只需要删除服务器上的 Session。

而 JWT 在完全无状态的情况下,一旦签发,在过期之前通常很难主动撤销。

JWT 的优势则是服务器之间不需要共享 Session,比较适合 API、微服务和分布式系统。

所以在实际项目中,不应该简单地问:

JWT 和 Session 谁更好?

更准确的问题应该是:

当前系统更适合哪一种认证方式?

最后

JWT 本身其实并不复杂。

它最核心的结构只有:

Header.Payload.Signature

Header 描述 Token 和签名算法,Payload 保存需要传递的数据,而 Signature 用来证明前面的数据没有被篡改。

真正需要注意的是,不要把 JWT 当成一种“加密用户数据”的技术。

JWT 默认情况下并不会隐藏 Payload。

它真正解决的问题,是让服务器能够验证:

这份 Token 是我签发的,而且里面的数据没有被修改。

理解这一点之后,再去看 JWT 的登录认证、权限校验、Access Token 和 Refresh Token,就会容易很多。

JWT 只是认证系统中的一个工具,而不是完整的安全方案。真正应用到项目里时,Token 的有效期、存储位置、撤销机制、刷新机制以及 HTTPS,往往比“用了 JWT”本身更加重要。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。