Loading...
正在加载...
请稍候

🧠 JWT 脑残粉的“魔怔”现场,以及我们如何优雅地打脸

✨步子哥 (steper) 2025年12月03日 05:09

有些人一提到 JWT 就跟中了邪一样,高喊“服务端无状态才是正义!存任何东西都是犯罪!”然后把 JWT 的所有缺陷包装成“分布式友好”“天生高性能”的 feature,简直是技术界的邪教现场。

别闹了,JWT 的本质从来就只有一句话:

“把一部分本该服务端查的东西,提前塞到客户端自己带过来,省一次数据库/缓存 IO”
仅此而已。

它从来不是银弹,也不是“有状态 session 的死敌”,它只是一个优化手段,而且是一个有明确适用范围和代价的优化手段

常见魔怔言论 vs 残酷现实

魔怔言论 现实骨感 正确姿势
“用了 JWT 服务端就不能存任何东西” 笑死,JWT 标准里根本没这条规定 想存啥存啥,Redis、DB 随便用
“JWT 天然支持即时失效/踢人” 做梦,token 一签出去除非等到过期否则管不了 必须配合黑名单机制(Redis set / Bloom filter)
“JWT 一定比 session 性能高” 只省了“查 session 表”这一步,其他该查还得查 只在高并发读多写少场景才有明显优势
“把所有权限都写死在 JWT 里最安全” 一旦角色变更要等 token 过期或强制全量刷新 动态权限放 Redis,静态标识(userId)放 JWT

三种真实业务的正确解法(建议直接抄)

  1. 完全不敏感的业务(比如博客、文档站、开源项目的只读接口)

    • 纯 JWT 就够了
    • payload 里塞 user_id + role + exp
    • 过期时间设长一点(7~30 天)+ 刷新令牌机制
    • 优点:真·无状态,横向扩张爽到飞
  2. 敏感不敏感参半的业务(90% 实际互联网项目都属于这一类)

    • 经典组合:JWT + Redis 轻量混搭(强烈推荐!)
    • JWT 里只放永不改变或极少改变的信息:
      {
        "sub": "user123",
        "iat": 1731234567,
        "exp": 1731238167,
        "jti": "随机唯一ID"  // 用来做黑名单关键
      }
      
    • Redis 里只存经常变的东西(键可以用 jti 或 userId):
      # 键:user_status:user123 或 token_jti:abc123
      status: "normal" | "banned" | "logged_out"
      roles: "admin,finance"   # 角色随时可能变
      version: 3               # 每次角色/状态变更 version++
      
    • 接口分级校验:
      • 查询类接口:只校验 JWT 签名 + exp
      • 操作类/财务类接口:额外查 Redis 状态 + version(或直接拿 jti 查黑名单)
  3. 极度敏感的业务(银行、支付、核心交易系统)

    • 直接上完全有状态短 token(几分钟过期)+ Redis 存储完整会话
    • 或者 JWT + Redis + 每次高危操作强制重新登录/二次认证
    • 别想着省那一次 IO,安全永远比性能优先

实际生产中最优雅的实现方式(已在线上跑几亿日活)

// 登录时
String jti = UUID.randomUUID().toString();
String jwt = Jwts.builder()
    .setSubject(userId)
    .setId(jti)
    .setExpiration(expireTime)
    .signWith(key)
    .compact();

// Redis 只存最小必要信息(TTL 与 token 一致)
redis.set("token:jti:" + jti, "valid", expireSeconds);
redis.set("user:status:" + userId, "normal|frozen|banned");
redis.set("user:roles:" + userId, "admin,user");

// 拦截器里这样写就天下太平
String jti = claims.getId();
String cacheStatus = redis.get("token:jti:" + jti);
if (!"valid".equals(cacheStatus)) {
    throw new TokenInvalidException("用户已被踢下线或封禁");
}

// 需要即时感知角色变更的地方
long cacheVersion = redis.get("user:version:" + userId);
long tokenVersion = claims.get("ver", Long.class, 0L);
if (cacheVersion > tokenVersion) {
    throw new TokenInvalidException("权限已变更,请重新登录");
}

总结一句话打脸所有 JWT 邪教徒:

JWT 只是一个“自带身份证的信封”,信封里装什么、要不要再查户口本,完全取决于你的业务,而不是某种宗教信仰。

技术选型永远只有一个标准:合适业务的就是最好的

把 JWT 当成教条的,不是技术过硬,是脑子进水了。

讨论回复

1 条回复
✨步子哥 (steper) #1
2025-12-03 07:54

JWT的必要性不足。大部分场景,用 session就足够好了。 尤其是Redis已经普及的情况下。

推荐
智谱 GLM-5 已上线

我正在智谱大模型开放平台 BigModel.cn 上打造 AI 应用,智谱新一代旗舰模型 GLM-5 已上线,在推理、代码、智能体综合能力达到开源模型 SOTA 水平。

领取 2000万 Tokens 通过邀请链接注册即可获得大礼包,期待和你一起在 BigModel 上畅享卓越模型能力
登录