她问我: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,要用好也不容易。

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

相关推荐
我最厉害。,。8 分钟前
接口安全&SOAP&OpenAPI&RESTful&分类特征导入&项目联动检测
后端·restful
穗余1 小时前
NodeJS全栈开发面试题讲解——P2Express / Nest 后端开发
前端·node.js
航Hang*1 小时前
WEBSTORM前端 —— 第3章:移动 Web —— 第4节:移动适配-VM
前端·笔记·edge·less·css3·html5·webstorm
江城开朗的豌豆1 小时前
JavaScript篇:a==0 && a==1 居然能成立?揭秘JS中的"魔法"比较
前端·javascript·面试
江城开朗的豌豆1 小时前
JavaScript篇:setTimeout遇上for循环:为什么总是输出5?如何正确输出0-4?
前端·javascript·面试
橘子味的冰淇淋~2 小时前
npm run build 报错:Some chunks are larger than 500 KB after minification
前端·npm·node.js
QING6182 小时前
Gradle 核心配置属性详解 - 新手指南(二)
android·前端·gradle
普通老人2 小时前
【前端】Vue中实现pdf逐页转图片,图片再逐张提取文字
前端·vue.js·pdf
QING6182 小时前
Gradle 核心配置属性详解 - 新手指南(一)
android·前端·gradle
AntBlack2 小时前
计算机视觉 : 端午无事 ,图像处理入门案例一文速通
后端·python·计算机视觉