JWT鉴权到底做了什么?
zijieLeo
约 19 分钟
7 次浏览
先看流程图(成熟版:access + refresh + Redis)
1)执行主体清单:谁对谁做了什么
-
执行主体:用户
- 作用对象:前端
- 动作:输入账号/密码、点击登录、触发敏感操作
-
执行主体:前端(浏览器端应用)
- 作用对象:后端接口、浏览器存储
- 动作:发起登录请求、保存 token、后续请求携带 token、在页面侧做路由守卫(更多是体验层)
-
执行主体:后端(登录接口 / 刷新接口 / 业务接口)
- 作用对象:数据库、Redis、JWT 签名验签、请求上下文
- 动作:校验凭证、签发 token、刷新 token、鉴权与授权、输出错误码
-
执行主体:数据库
- 作用对象:用户/角色/状态等数据
- 动作:查用户、取密码哈希、取角色、判断是否禁用/锁定
-
执行主体:Redis
- 作用对象:会话状态、限流计数、撤销/黑名单、并发刷新控制
- 动作:存 refresh 会话、做 rate limit、做 refresh 轮换、做“踢下线/封禁”即时生效
2)为什么要 Redis:成熟流程里它在干什么
JWT 的问题不在“验签”,而在“签发之后怎么管”。
2.1 解决“撤销”这件事
JWT 自包含,只要没过期,单靠验签就会通过。现实里经常要:
- 改密码要立刻让旧登录态失效
- 封号要立刻生效
- 发现异常登录要踢下线
Redis 常见落点:
-
refresh_token 会话落 Redis(最常见)
- access_token 设短(比如 5~15 分钟)
- refresh_token 设长(比如 7~30 天)
- 每次 refresh 都要查 Redis:会话不存在/被撤销 → 刷新失败 → 登录态结束
- 这样“长期登录态”是可控的,不靠 JWT 自己硬扛
-
每次请求查 Redis(更强控制)
- JWT payload 带
sessionId/jti/version - Redis 维护该用户当前有效会话/版本
- 每次请求验签后再查 Redis,不在白名单就拒绝
- 代价是多一次 Redis 读,但换来“想让它立刻失效就立刻失效”
- JWT payload 带
2.2 做风控:限流 + 并发刷新控制
- 登录限流:防爆破(计数 + TTL)
- 刷新并发锁:同一时间多个请求同时刷新,容易把 refresh rotation 搞乱,用 Redis 锁最稳
- 黑名单/异常标记:检测到 refresh 被复用(疑似泄漏)直接封掉会话
3)双 Token(access + refresh):为什么成了主流
单 token(只一个 JWT)
- 优点:简单
- 缺点:要么过期短体验差,要么过期长泄漏代价大;撤销困难
双 token(access 短 + refresh 长)
- 优点
- access 泄漏窗口小
- access 过期可静默刷新,体验好
- refresh 可撤销(靠 Redis/数据库会话),能踢下线
- 缺点
- 工程复杂度上来:刷新接口、rotation、并发刷新、异常处理
成熟系统看起来“顺滑”,通常就是因为 refresh 这条链路做得扎实。
4)Token 放哪:为什么会牵扯 XSS/CSRF(原理 + 防法)
这是选择题:你选的存储位置,会把风险偏向不同方向。
4.1 放 localStorage:更怕 XSS
- XSS 怎么发生:页面里有恶意脚本执行(用户输入没消毒、富文本、第三方脚本、Markdown 渲染等)
- 为什么能偷 token:token 在 localStorage,JS 直接读出来就能发走
- 怎么防(核心原理)
- 不让“数据变成脚本”:严格消毒/转义
- CSP 把脚本来源钉死,尽量禁 inline
- 第三方脚本和依赖治理
- 富文本/Markdown 渲染特别要小心
4.2 放 Cookie + HttpOnly:更怕 CSRF,但更抗“偷 token”
- HttpOnly 的意义:JS 读不到 cookie 值,所以 XSS 很难直接把 token 字符串掏走
- CSRF 为什么会来:浏览器会自动携带 Cookie;攻击者诱导用户访问恶意站点,那个站点向你后端发请求,浏览器可能会带上 Cookie
- 怎么防(核心原理)
SameSite=Lax/Strict:减少跨站自动携带- CSRF Token:攻击者拿不到你站点生成的随机 token
- 校验
Origin/Referer:跨站来源能暴露出来
4.3 不同场景的常见选择(更贴近真实工程)
- 高价值账户(金融/支付/企业核心后台)
- 多见:Cookie + HttpOnly + SameSite + CSRF Token + 强 CSP
- 逻辑很朴素:宁愿把 CSRF 这套工程成本背起来,也尽量别让 token 暴露在 JS 可读的位置
- 内容型产品 / 个人博客后台
- 两种都能跑:
- localStorage + Authorization:实现快,但要把 XSS 入口盯紧
- HttpOnly Cookie:更抗“偷 token”,但 CSRF 防护要配齐
- 两种都能跑:
- 移动端 App
- 用系统安全存储(Keychain/Keystore),CSRF 不是典型问题,但仍要防本地泄漏与重放
5)NestJS / Java 对比:本质做的是同一件事
-
执行主体:NestJS
- 用 Strategy/Guard 把“提取 token → 验签 → 注入当前用户 → 授权”组件化
- Redis 常用于:session、rate limit、refresh rotation、blacklist
-
执行主体:Java(Spring Security)
- 用 Filter 链把“提取 token → 验签 → 写入安全上下文 → 授权规则”做成全局统一
- Redis 常用于:分布式会话、token 撤销、限流、风控状态
换掉框架名,动作完全一致:每个请求先过鉴权层,鉴权层决定你是谁、能不能做这个事。
结尾:我现在判断“JWT 鉴权成熟不成熟”,就看三点
- access 是否短命、refresh 是否可控(能撤销、能轮换)
- token 存储策略是否和 XSS/CSRF 防线匹配
- Redis 是否承载了会话与风控状态(否则很难“管理登录态”)
这三点做齐,JWT 才不只是“能登录”,而是“能长期稳定跑”。
评论
登录后即可发表评论