JWT原理分析
JWT 认证完整链路——从登录到退出的每一步都有一个真实项目在跑我做过一个政务系统的认证模块Java 从零实现了一套 JWT 认证——双 Token、黑名单、Cookie 和 Header 双通道提取、用户信息缓存、权限鉴权。这篇文章拆开这个模块的完整源码从 Token 结构到每条请求走过的每一步每一步都给出来源和原因。文章目录JWT 认证完整链路——从登录到退出的每一步都有一个真实项目在跑一、为什么不用 Session二、Token 结构——三段 Base64 拼起来的不只是一串字三、生成 Token——两个方法两种生命周期四、refreshToken 换发——Token 时效的权衡五、验证 Token——一行 parse 背后的三步六、完整请求链路——一条请求从进来到出去的 7 步七、黑名单——无状态的代价和补救八、Token 的两个通道——Header 和 Cookie 各自的用途九、认证和鉴权是两件事十、配置十一、结语一、为什么不用 SessionSession 是服务端状态。用户登录后服务端在内存或 Redis 里存一份session_id → 用户信息的映射浏览器通过 Cookie 回传 session_id。这个模型在单服务器时代够用。但政务系统有几个特点多节点部署——如果走 Session要么做 sticky session绑定到固定节点那台挂了全掉要么做 session 共享引入 Redis 集中存储单点故障风险Cookie 跨域限制——Cookie 的 domain 绑定了一个域名。如果系统有多个子域名app1.gov.cn、app2.gov.cn同域名下的 Cookie 不能跨子域共享——而 Session 依赖 Cookie前后端分离——前端可能是独立的 Vue/React 应用跟后端不同域。Session 的 Cookie 在同源策略下传不到后端JWT 解决了这三个问题Token 是自包含的——用户信息签名后放在 Token 里服务端不存状态。任何节点拿到 Token 用同一个密钥验签就能还原用户身份。Token 通过 Authorization Header 传不受 Cookie 同源策略限制。代价Token 一旦签发就无法主动失效不额外存储状态的话。这个代价引出了接下来的双 Token 黑名单设计。二、Token 结构——三段 Base64 拼起来的不只是一串字一个 JWT Token 长这样eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJhZG1pbiJ9.HN_sWbXG-8_zpHjz5-TGKl06VfnKnCpN6A9TknR8VFs用句号拆开是三段Header → eyJhbGciOiJIUzI1NiJ9 Payload → eyJzdWIiOiJhZG1pbiJ9 Sig → HN_sWbXG-8_zpHjz5-TGKl06VfnKnCpN6A9TknR8VFsBase64 解码前两段Header{alg:HS256,typ:JWT}Payload{sub:admin,psnId:admin,psnName:管理员,unitId:U001,type:access,iss:browise,iat:1781683991,exp:1781691191}Signature 的计算方式HMAC-SHA256( Base64(Header) . Base64(Payload), secret )关键理解Payload 是明文 Base64任何人都能解出来看。Token 的安全性不靠加密——靠签名。没有 secret 的人可以读到 Payload 里的psnIdadmin——但他改不了。他如果把psnId改成superadmin然后重算 Base64——签名就对不上了——服务端一验就拒绝。所以 JWT 的设计哲学是Payload 可以公开看但不能改。三、生成 Token——两个方法两种生命周期登录成功后生成两个 Token。accessToken2 小时// 来源: JwtUtil.javapublicStringgenerateAccessToken(StringpsnId,StringpsnName,StringunitId){MapString,ObjectclaimsnewHashMap();claims.put(psnId,psnId);claims.put(psnName,psnName);claims.put(unitId,unitId);claims.put(type,access);// 标记类型returnJwts.builder().setClaims(claims).setSubject(psnId).setIssuer(browise).setIssuedAt(newDate()).setExpiration(newDate(System.currentTimeMillis()7200000))// 2小时.signWith(SignatureAlgorithm.HS256,secret).compact();}refreshToken7 天publicStringgenerateRefreshToken(StringpsnId){MapString,ObjectclaimsnewHashMap();claims.put(psnId,psnId);claims.put(type,refresh);// 标记类型——和 access 区分returnJwts.builder().setClaims(claims).setSubject(psnId).setIssuer(browise).setIssuedAt(newDate()).setExpiration(newDate(System.currentTimeMillis()604800000))// 7天.signWith(SignatureAlgorithm.HS256,secret).compact();}两个 Token 的区别不只是过期时间——type字段在验证时用来区分typerefresh的 Token 不能当 accessToken 用——它只能用来换新 Token。如果你把 refreshToken 放进 Authorization Header 调业务接口——Filter 层检查 type 不是 “access” 直接拒绝。四、refreshToken 换发——Token 时效的权衡为什么需要两个 TokenaccessToken 只有 2 小时因为它是高频传输的——每次请求都带着。如果 accessToken 有效期太长比如 7 天一旦泄露攻击者有 7 天时间可以任意调用接口——而 JWT 本身不存储状态你没法主动让它失效。但 2 小时太短——用户在一天之内可能被强制登出好几次体验极差。所以用一个 7 天的 refreshToken 来解决accessToken 过期后前端拿 refreshToken 去/auth/refresh换一个新的 accessToken——不用重新登录。accessToken 过期2小时 │ ├─ 前端请求 /api/xxx → 401 │ ├─ 前端拦截 401拿 refreshToken 调 /auth/refresh │ ├─ 验证 refreshToken 签名 有效期 │ ├─ 检查 refreshToken 是否在黑名单logout 时加入 │ ├─ 通过 → 生成新的 accessToken 返回 │ └─ 不通过 → 前端跳转登录页 │ └─ 前端拿到新 accessToken → 重试原请求refreshToken 是 httpOnly 的 Cookie——JS 不能读。这样即使前端被 XSS 攻击拿到了 accessToken攻击者也只能用 2 小时。要长期控制需要拿到刷新能力——但 refreshToken 在 httpOnly Cookie 里XSS 拿不到。五、验证 Token——一行 parse 背后的三步// 来源: JwtUtil.javapublicClaimsparseToken(Stringtoken){returnJwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();}parseClaimsJws背后做了三件事用 secret 重算签名——和 Token 里的 Signature 段比对。不匹配 → 抛出SignatureException——Token 被篡改过检查 exp——当前时间 exp → 抛出ExpiredJwtException——Token 已过期解析 Payload——验证通过后返回Claims对象——也就是 Payload 里的所有字段三步在一行代码里完成。如果任何一步失败——Filter 直接返回 401。六、完整请求链路——一条请求从进来到出去的 7 步JwtAuthFilter 拦截所有请求除了登录和公开接口。每条请求走 7 步HTTP 请求到达 │ ├─ ① 提取 Token │ ├─ Authorization: Bearer xxx → 截取 Bearer 之后的部分 │ └─ Cookie: BROWISE_ATxxx → 从 Cookie 取值备用通道 │ ├─ ② 验证 Token │ ├─ parseToken(token) → 签名校验 过期检查 │ └─ 解析 Claims → 拿到 psnId, type, iat │ ├─ ③ 黑名单检查jwtBlacklist │ ├─ isInvalid(psnId, iat) → 该用户此时刻之前的 Token 全失效 │ └─ isInvalidByToken(token) → 精确失效logout 单设备 │ ├─ ④ 加载用户信息userProfileCache │ ├─ 查 OP_PERSON → psnName, unitId, unitName │ ├─ 查 SYS_USER.STATUS → 账号是否启用 │ ├─ 查 SYS_USER_ROLE → 角色列表 │ └─ 缓存 1 小时TTL 内直接返回不查库 │ ├─ ⑤ 注入当前用户CurrentUser.set(profile) │ └─ ThreadLocal → 请求线程内全局可访问 │ ├─ ⑥ 账号状态检查 │ └─ !profile.isEnabled() → 403 账号已禁用 │ └─ ⑦ 权限检查menuAuthProvider.canAccess(path) ├─ 路径在 allowIdList 白名单 → 跳过鉴权 └─ 路径需匹配用户角色的菜单权限 → 不匹配返回 403第三步的黑名单是最容易被忽略的设计——它解决了Token 已经签发了但需要让它失效的问题。七、黑名单——无状态的代价和补救JWT 不存状态。但业务上需要主动失效——用户修改密码、管理员禁用账号、用户主动登出——这些场景要求已经签发的 Token 立刻不能用了。这个系统里黑名单分两层第一层用户级失效按签发时间当用户修改密码或管理员重置密码时——把这个用户的(psnId, 当前时间戳)写入黑名单。此后验证逻辑这样判断publicbooleanisInvalid(StringpsnId,longiat){LongcutoffTimeblacklist.get(psnId);// 从 Map 取该用户的最早失效时间if(cutoffTimenull)returnfalse;// 不在黑名单 → 正常returniatcutoffTime;// Token 签发时间 ≤ 失效时间 → 已失效}如果用户在 12:00 改了密码——写入blacklist[admin] 12:00。之后所有iat ≤ 12:00的 Token 全部作废——不管你是手机登录的还是浏览器登录的。因为改了密码意味着所有历史登录都不可信。第二层Token 级失效登出单设备用户点击退出登录——把当前这个 Token 写入精确黑名单publicbooleanisInvalidByToken(Stringtoken){returntokenBlacklist.contains(token);}logout 只失效当前设备——手机端的 Token 不受影响。黑名单的存储选择在演示环境里黑名单是内存 Map——服务重启后黑名单清空。生产环境换成 Redis——设置 key 的 TTL 等于 Token 的剩余有效时间——Token 过期后 key 自动删除不用手动清理。八、Token 的两个通道——Header 和 Cookie 各自的用途Token 的传输用了两个通道通道Token 类型场景为什么Authorization HeaderaccessTokenAJAX 请求不受 Cookie 同源限制跨域可用httpOnly CookieaccessToken页面跳转、form 提交浏览器自动携带不用前端手动加httpOnly CookierefreshToken刷新接口JS 不可读——XSS 攻击拿不到两个通道的原因是——header 方式需要前端在每个请求里手动加Authorization: Bearer xxx。但有些场景比如表单 POST 提交、a标签跳转前端控制不了 header。这时候 Cookie 作为备用通道——浏览器自动附在请求上——Filter 层两个地方都查有任意一个就继续验证。九、认证和鉴权是两件事很多刚接触权限系统的开发者把认证和鉴权混为一谈。这个模块里它们是两个独立的步骤认证Authentication 你是谁 → ①②③④⑤提取 Token → 验签 → 黑名单 → 加载用户 → 注入上下文 鉴权Authorization 你能做什么 → ⑦拿到用户角色和当前请求路径 → 匹配 → 放行或拒绝为什么分开因为有些接口不需要鉴权——只要你是登录用户就可以访问比如查看自己的个人信息。有些接口只需要认证不需要角色判断——比如全员可见的公告。十、配置browise:auth:enabled:truejwt:secret:browise-demo-local-dev-secret-key-must-be-32charsaccess-token-expire-ms:7200000# 2小时refresh-token-expire-ms:604800000# 7天issuer:browiseexclude-paths:-/auth/**-/captcha-/loginsecret在生产环境不应该写在配置文件里。放在环境变量或密钥管理服务中注入——${JWT_SECRET}。十一、结语JWT 认证不是一个引入一个库、写两行代码的事情。它的核心设计决策——双 Token、黑名单、双通道传输、认证鉴权分离——都是在回答同一个问题“不存状态的 Token 怎么安全地用”accessToken 短有效防止泄露窗口、refreshToken 长有效避免频繁登录、httpOnly Cookie 防 XSS、黑名单补上无状态的漏洞、用户缓存省掉每次查库——这五个设计合在一起才是一个生产可用的 JWT 认证模块。✅ 亮点从为什么不用 Session的自然动机出发按照请求链路逐层拆开 JWT 认证的完整实现——双 Token 生命周期、黑名单两级失效、Header/Cookie 双通道传输、认证鉴权分离——每层都给出了真实源码和为什么选这个设计的原因。适合正在实现或重构认证模块的开发者也适合面试中你们系统怎么做登录的的系统级回答。扩展方向OAuth2 授权码模式在政务系统中的应用、多系统单点登录SSO的 Token 共享方案、JWT Redis 组合实现有状态 Token 的折中方案。

相关新闻

容量测试到底测什么——一次对话理清同时在线和并发请求

容量测试到底测什么——一次对话理清同时在线和并发请求

容量测试到底测什么?一次对话理清"同时在线"和"并发请求" 和同事讨论容量测试,发现很多人把"同时在线"和"并发请求"搅在一起。这篇把这段对话记录下来,帮你看清容量的本质。 文章目录容量测试到底测…

2026/7/30 0:47:02阅读更多 →
大厂面试,自进化 agent 正在成为主流!

大厂面试,自进化 agent 正在成为主流!

最近社区学员反馈一些Agent 面经时,发现自进化 agent正在成为主流!今天从一道字节算法二面的题开始,带你看懂大厂真正想要什么样的人才能力。 👔 面试官:“human feedback 是怎么被 agent 消化吸收的?” …

2026/7/30 0:47:02阅读更多 →
AI提示词黄金模板库(覆盖12大行业+8类任务):2024最新实战验证版,仅开放72小时

AI提示词黄金模板库(覆盖12大行业+8类任务):2024最新实战验证版,仅开放72小时

更多请点击: https://codechina.net 第一章:AI提示词黄金模板库总览与核心设计哲学 AI提示词并非随意拼凑的语句,而是融合语言学、认知科学与工程实践的精密接口。黄金模板库的本质,是将人类意图结构化、可复用、可迭代的表达范式…

2026/7/30 0:47:02阅读更多 →
2025年AI写作工具横评:DeepSeek导出Word,谁才是真正的“格式救星”?

2025年AI写作工具横评:DeepSeek导出Word,谁才是真正的“格式救星”?

2025年,AI大模型的应用已从“尝鲜”迈向“刚需”。特别是随着DeepSeek等推理模型的普及,生成包含复杂数学推导、多层级表格的技术文档已成为常态。然而,在从“AI生成”到“本地文档”的最后一公里,无数用户却遭遇了“滑铁卢”。 明…

2026/7/30 2:04:26阅读更多 →
Qt开发中QString与容器类的核心用法与性能优化

Qt开发中QString与容器类的核心用法与性能优化

1. QString在Qt开发中的核心地位QString作为Qt框架中最基础也最常用的字符串处理类,几乎出现在每一个Qt项目的代码中。不同于标准C的std::string,QString从设计之初就充分考虑了国际化、编码转换和跨平台需求。我经历过从MFC到Qt的迁移项目,最…

2026/7/30 2:04:26阅读更多 →
Python志愿者管理系统:Django+React实现智能排班与调度

Python志愿者管理系统:Django+React实现智能排班与调度

1. 项目概述:Python社区志愿者服务系统的核心价值去年参与组织PyCon线下活动时,我深刻体会到志愿者管理的痛点:报名信息散落在多个Excel表格,排班靠微信群接龙,突发状况全靠人肉协调。这个用DjangoReact开发的志愿者管…

2026/7/30 2:04:26阅读更多 →
2026免费空间大的网盘软件有哪些推荐,主流网盘拆解

2026免费空间大的网盘软件有哪些推荐,主流网盘拆解

日常存照片、存学习资料、存办公文件,一款空间大、好用、稳定的网盘是刚需。市面上网盘产品众多,很多人不知道哪款免费空间足、实用性高。今天给大家实测分享几款主流大容量网盘,全方位对比测评,帮大家选到适配自己的存储工具。一…

2026/7/30 2:04:26阅读更多 →
终极指南:如何快速免费备份你的QQ空间完整数据

终极指南:如何快速免费备份你的QQ空间完整数据

终极指南:如何快速免费备份你的QQ空间完整数据 【免费下载链接】QZoneExport QQ空间导出助手,用于备份QQ空间的说说、日志、私密日记、相册、视频、留言板、QQ好友、收藏夹、分享、最近访客为文件,便于迁移与保存 项目地址: https://gitcod…

2026/7/30 2:04:26阅读更多 →
物理AI+ Genesis AI联创王尊玄 | 不卷大模型卷系统能力

物理AI+ Genesis AI联创王尊玄 | 不卷大模型卷系统能力

机器人一定要长得像人吗?Genesis AI 的回答是,不一定。 Genesis AI 这家base在法国和硅谷的具身新星,今年6月推出了首款具身机器人Eno,没有头和脸,只保留灵巧操作必要的部分,流畅简洁的设计被称为机器人界的…

2026/7/30 2:02:26阅读更多 →
覆盖国产 + 海外 + 开源模型,OpenClaw 2.7.9 Windows/Mac 双端部署详解

覆盖国产 + 海外 + 开源模型,OpenClaw 2.7.9 Windows/Mac 双端部署详解

🔹 工具基础介绍 OpenClaw 是开源生态中一款实用性较强的本地智能工具,凭借本地离线运行、可视化图形操作和任务自动化三大核心特性,赢得了众多用户的青睐。与普通在线对话AI工具不同,它属于能够直接操控本机软硬件的智能数字员工…

2026/7/29 9:47:45阅读更多 →
伺服阀焊完微漏毁整机?精密激光焊接三关锁住高压

伺服阀焊完微漏毁整机?精密激光焊接三关锁住高压

所谓液压伺服阀体的精密激光焊接,是用激光束对阀座壳体(通常为不锈钢或铝合金)进行密封焊接,使阀体在21-35MPa的高压液压油或压缩气体中长期运行而不发生介质泄漏。液压伺服阀是高端液压系统的"大脑"。从航空航天飞行控…

2026/7/29 7:00:19阅读更多 →
D2DX:三步实现《暗黑破坏神2》高清宽屏体验的终极指南

D2DX:三步实现《暗黑破坏神2》高清宽屏体验的终极指南

D2DX:三步实现《暗黑破坏神2》高清宽屏体验的终极指南 【免费下载链接】d2dx D2DX is a complete solution to make Diablo II run well on modern PCs, with high fps and better resolutions. 项目地址: https://gitcode.com/gh_mirrors/d2/d2dx 你是否还在…

2026/7/29 7:58:51阅读更多 →
3分钟解锁iOS应用自由:TrollInstallerX让你的iPhone摆脱安装限制 [特殊字符]

3分钟解锁iOS应用自由:TrollInstallerX让你的iPhone摆脱安装限制 [特殊字符]

3分钟解锁iOS应用自由:TrollInstallerX让你的iPhone摆脱安装限制 🚀 【免费下载链接】TrollInstallerX A TrollStore installer for iOS 14.0 - 16.6.1 项目地址: https://gitcode.com/gh_mirrors/tr/TrollInstallerX 你是否曾经因为iOS系统的严格…

2026/7/30 0:00:58阅读更多 →
[GESP202606 四级] 扫雷

[GESP202606 四级] 扫雷

B4557 [GESP202606 四级] 扫雷 https://www.luogu.com.cn/problem/B4557 中国计算机学会(CCF)2026年6月C四级讲解——扫雷 https://www.bilibili.com/video/BV1MCMg6AEXR/ B4557 [GESP202606 四级] 扫雷 https://www.bilibili.com/video/BV1ZKTj6ZEVh/ 2…

2026/7/30 0:00:58阅读更多 →
Windows驱动存储终极清理工具:DriverStoreExplorer完全指南

Windows驱动存储终极清理工具:DriverStoreExplorer完全指南

Windows驱动存储终极清理工具:DriverStoreExplorer完全指南 【免费下载链接】DriverStoreExplorer Driver Store Explorer 项目地址: https://gitcode.com/gh_mirrors/dr/DriverStoreExplorer 您是否曾因Windows系统盘空间不足而烦恼?是否遇到过设…

2026/7/30 0:00:58阅读更多 →
YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

如果你在部署 YOLOv8 时,发现推理速度只有可怜的 1-2 FPS,而别人的演示视频却能跑到 30 FPS 以上,那么问题很可能不在模型本身,而在于你的整个处理链路。很多开发者拿到一个训练好的 YOLOv8 模型后,会直接使用官方示例…

2026/7/30 0:27:26阅读更多 →
Coze与Dify对比指南:低代码AI应用开发从入门到实战

Coze与Dify对比指南:低代码AI应用开发从入门到实战

1. 从零到一:为什么你需要了解 Coze 和 Dify?如果你对 AI 应用开发感兴趣,但一看到“大模型”、“智能体”、“工作流”这些词就头疼,觉得门槛太高,那这篇文章就是为你准备的。很多开发者,包括我自己&#…

2026/7/29 4:31:51阅读更多 →
AI生图工具怎么选?2026年6月版实测对比

AI生图工具怎么选?2026年6月版实测对比

做自媒体的朋友应该都有体会:配图一直是个让人头疼的问题。2026年,AI生图工具已经非常成熟了,但工具太多反而不知道怎么选。以下是截至2026年6月我对主流AI生图工具的实测对比。Midjourney V8.1:速度之王2026年6月11日&#xff0c…

2026/7/29 14:26:42阅读更多 →