她问我:JWT里到底能塞多少东西?我说我不知道……

开篇 · 413问题

夜已深,办公室只剩几盏灯还亮着。我刚刚提交了一个小改动,准备收拾东西走人。小语端着速溶咖啡走过来,在我椅子旁边坐下,皱着眉头盯着屏幕。

"豆包,你们后端的JWT里都塞了些什么?我这边登录后第一次请求总是很慢。甚至还有日志显示413错误------Payload Too Large"

我转过头看她,"就用户信息啊,用户ID、角色、权限什么的。怎么了?"

"啧,豆包,你这Token是打算在HTTP头里开博览会吗?"小语的声音带着刚加完班的倦意,她端着马克杯晃到我身后,杯沿氤氲的热气模糊了镜片。"八哥那边炸锅了,新注册用户用APP登录,授权页直接卡死,后台日志里堆满了Request Header Fields Too Large。他刚在群里咆哮,说你的Token快比用户协议还长了。"

小语缓缓喝了一口咖啡,"你知道JWT里能放多少数据吗?"

说实话,我还真没仔细想过这个问题。平时就是照着网上的教程,该放什么就放什么,从来没考虑过大小限制。

"JWT 里的 Claim 能装多少东西啊?"我顺着话题问,"我知道它就是个 Base64 编码的字符串,里面有头、负载和签名三个部分,应该没硬限制吧?"

JWT 本身并没有硬性规定每个 Claim 能存放的数据量

解决篇

这时阿辰从会议室出来,听到我们的对话。"你们在聊JWT?"他走过来,看了看小语的屏幕,"Token确实有点大。豆子,你在Claim里放了多少东西?"

我有点心虚,"也不多啊,就按规范加了sub(用户ID)、rolesexp(过期时间)、iat(签发时间)...就额外加了个profile对象,里面塞了标签数组和活动记录......"

"标签数组?活动记录?还三次?"阿辰挑了挑眉,"标签是动态增长的吧?活动记录只会越来越长吧?你打算让每个HTTP请求都扛着这么个不断增重的沙袋满世界跑?"

"可是..."我试图辩解,"Claim理论上能放不少东西啊......"

"你知道HTTP请求头有大小限制吗?大部分服务器限制在8KB到16KB之间。JWT是放在Authorization头里传输的 。如果超了,轻则413错误,重则直接截断或400。你这Token已经踩线了。"阿辰放下保温杯,屏幕光映着他冷静的脸。"JWT的本质是轻量凭证,不是数据搬运车。 "

"更关键的是传输成本。" 阿辰补充道,语气像在陈述一个基础定律,"每个API请求,这个巨大的字符串都要在客户端、CDN、网关、微服务之间完整传输一次。多1KB Token,千次请求就多传输1MB数据。用户基数上来,流量费和延迟都是看得见的损耗。"

小语点点头,"难怪我感觉请求变慢了,每次都要传输这么大的Token。"

我有点傻眼,"那应该放多少?"

"一般建议控制在1KB以内,最多不超过2KB。"阿辰坐下来,"JWT的设计初衷是传递关键的声明数据,不是用来存储大量信息的。"

阿辰继续说:"你想想,每个API请求都要带着这个Token,如果Token很大,网络传输的开销就很明显。JWT本身是Base64编码的,包含Header、Payload和Signature三部分,你的数据主要在Payload里。"

"那应该放什么?"我虚心请教。

"Claim是声明(Claims),不是仓库(Data Store)。 " 阿辰指尖在桌面轻点,像在敲击无形的逻辑链,"存放关键标识符 (如sub用户ID),而非完整数据。需要大块信息时,用这个ID去查缓存或数据库------这是空间换时间,也是安全边界。 "

他顿了下,目光转向我,"敏感信息(明文密码、身份证号、手机号)、实时状态(登录次数)、大文本/图片... " 他每说一项,就像在列举一个不该出现在Token里的违禁品,"这些要么有泄露风险(Token虽签名但可解码),要么破坏轻量原则。令牌如刀,贵在轻、快、利,不在重、大、全。 "

我恍然大悟,"所以是存标识,不是存实际数据?"

"对,这样既保持了JWT的轻量性,又满足了功能需求。"阿辰笑了笑,"还有个重要点,不要在JWT里放敏感信息。虽然有签名验证,但Payload是可以被解码的。"

小语补充道:"像密码、身份证号这些绝对不能放。我见过有人把用户的完整资料都塞进JWT里,简直是灾难。"

接下来的半个小时,我重新调整了JWT的结构。删除了冗余的用户详细信息,整个Token的大小从原来的10KB压缩到了不到500字节:

  1. 剥离臃肿数据 :坚决移除了userProfile这个大对象,特别是动态标签和活动记录。

  2. 核心精简:Claim只保留最必要字段:

    • sub (用户ID - 核心标识)
    • preferred_username (用户名 - 简短展示用)
    • roles (角色/权限 - 数组但确保精简)
    • iat (签发时间)
    • exp (过期时间)
  3. 新增按需查询接口 :为下游服务设计高效API:GET /api/users/{userId}/profile,通过userId(即Token里的sub)按需获取完整画像和活动记录,并引入Redis缓存优化查询。

  4. 代码重构:Token生成严格约束,清晰如契约

java 复制代码
String token = Jwts.builder()
    .setSubject(username)
    .claim("userId", userId)
    .claim("role", primaryRole)  // 只放主要角色
    .setIssuedAt(new Date())
    .setExpiration(expireDate)
    .signWith(SignatureAlgorithm.HS512, secretKey)
    .compact();

🎉撒花篇

小语重新测试了登录和API请求,"明显快了!第一次请求的延迟基本消失了。"

系统重新部署后,用户反馈登录体验变好了,页面加载也更流畅。监控数据显示,API请求的平均响应时间降低了约15%。虽然看起来不多,但对于高频使用的系统来说,这个优化还是很有意义的。

夜已经深了,小语收拾东西准备走,路过我桌子时拍了拍我的椅背:"豆包,记住了哦,JWT要轻装上阵。"

我点点头,关闭电脑的时候想着,技术的细节往往藏在这些不起眼的地方。一个小小的Token大小问题,背后其实涉及网络传输、用户体验、系统架构等多个方面。看似简单的JWT,要用好也不容易。

阿辰锁好会议室,走过来说:"优化是个持续的过程,发现问题解决问题,这就是成长。"

相关推荐
涡能增压发动积17 小时前
同样的代码循环 10次正常 循环 100次就抛异常?自定义 Comparator 的 bug 让我丢尽颜面
后端
云烟成雨TD17 小时前
Spring AI Alibaba 1.x 系列【6】ReactAgent 同步执行 & 流式执行
java·人工智能·spring
Wenweno0o17 小时前
0基础Go语言Eino框架智能体实战-chatModel
开发语言·后端·golang
于慨17 小时前
Lambda 表达式、方法引用(Method Reference)语法
java·前端·servlet
石小石Orz17 小时前
油猴脚本实现生产环境加载本地qiankun子应用
前端·架构
swg32132117 小时前
Spring Boot 3.X Oauth2 认证服务与资源服务
java·spring boot·后端
从前慢丶18 小时前
前端交互规范(Web 端)
前端
tyung18 小时前
一个 main.go 搞定协作白板:你画一笔,全世界都看见
后端·go
gelald18 小时前
SpringBoot - 自动配置原理
java·spring boot·后端
CHU72903518 小时前
便捷约玩,沉浸推理:线上剧本杀APP功能版块设计详解
前端·小程序