CodeRoad Map 程序员的学习成长路线图。内容免费开放,登录后可同步路线图与课时进度。
由 程序员柏风 独立维护 · 关于作者
学习 更多
OAuth vs 本站 JWT 登录 Sa-Token 密码登录 vs 联邦登录场景。
OAuth vs 本站 JWT 登录
这一课你将能做到
用一张场景矩阵说清:什么时候自研 JWT 密码登录、什么时候 OAuth/OIDC 联邦、什么时候 Resource Server 开放 API
深入对比本站自研 JWT 方案与标准 OAuth2:解决什么问题、凭证谁签发、无状态的利与弊、撤销怎么做
设计本站方案的 refresh 轮换与撤销策略,并能回答「何时该把密码轨升级成 OAuth 轨」
为什么要学
本站日常跑的是:密码 + 自研 JWT ——用户提交账号密码,backend 校验后签发 JWT(带 uid、角色、过期时间),前端带着它调受保护接口。这套方案支撑了 platform 与 admin 的全部站内功能。
本 track 前面 9 课讲的 OAuth 解决的是另外两件事:联邦认人(用 GitHub/Google 登录)与对外授权(第三方应用调你的 API)。
新人最容易把两套词搅在一起:「都是 Bearer token,是不是一个东西?」分不清的代价很实际:排错时分不清 401 是本站会话挂了、IdP token 过期、还是开放 API scope 不够;设计时会把 IdP 的 access_token 错当站内钥匙。这一课把天平彻底摆平——面试里「JWT 和 OAuth 的关系」「JWT 无状态的弊病怎么补救」也全在这道题里。
先建立图像
┌─ 密码登录 ──► backend 校验 ──► 签发自研 JWT ──┐
用户 ─┤ ├─► /admin/** 业务接口
└─ GitHub/Google ──► OAuth 联邦认人 ──► 映射 User ──► 签发自研 JWT ──┘
(两条入口轨,同一张站内凭证)
外部系统 ── OAuth access_token(IdP 签发)──► /api/v1/** Resource Server
(自研 JWT 在这条链上无效)
两条轨逐维对比:
维度 本站自研 JWT 登录 OAuth 2.0 解决的问题 本站用户的认证与会话 第三方代表用户访问资源 / 联邦身份 参与方 用户 + 你的 backend(2 方) 用户、Client、AS、RS(4 角色) 凭证签发者 本站 backend(自家密钥) Authorization Server 凭证含义 「此人已登录,角色是 X」 「用户同意该 App 以 scope Y 访问资源 Z」 撤销难度 天然难点(见下),靠短寿命+黑名单+版本号 refresh 轮换、revocation 端点、grant 管理页 典型场景 单站 Web、管理后台、前后端分离 第三方登录、开放 API、多系统 SSO
一句话辨析(面试可直接用):「JWT」是 token 的编码格式,「OAuth」是授权协议——OAuth 的 access_token 经常就是 JWT,二者不是对立选项,而是不同层面的概念。
跟着做
步骤 1:给本站每个接口归类
POST /auth/login → 本站认证轨(校验密码,签发自研 JWT)
GET /admin/tracks → 本站会话轨(验自研 JWT + 角色)
GET /oauth2/authorization/github → OAuth 轨入口(联邦登录)
GET /api/v1/tracks → Resource Server 轨(若启用开放 API)
步骤 2:解剖本站自研 JWT 方案的完整闭环
登录:POST /auth/login {username, password}
→ backend 查库校验(BCrypt 比对)
→ jwtUtil.sign(uid, role, exp=2h) // 自家密钥 HS256/RS256 签发
→ 返回 JWT;前端写 HttpOnly cookie
请求:GET /admin/tracks + Cookie/Bearer
→ JWT 过滤器验签 + 查 exp + 读角色
→ 通过则 Controller 执行(无状态,不查 session)
步骤 3:直面「无状态」的利与弊(本课核心) 利:
① 不存会话:backend 重启、扩容都不掉登录态
② 水平扩展:多实例无需共享 session 存储
③ 高 QPS:验签是本地 CPU 操作,不打 Redis/DB
④ 跨服务:微服务网关验一次签,下游信任 claims
弊:
① 签发后无法单方面作废——封号、改密、登出后,旧 token 依然有效到 exp
② payload 只是 base64,不是加密——敏感信息绝不能放进去
③ 续期要做额外设计(refresh / 滑动续期),否则用户频繁被登出
步骤 4:JWT 撤销的三种补救(本站可落地) 方案 A 短寿命 + refresh:JWT 15 分钟,refresh 7 天存 Redis(可删=可撤销)
方案 B 黑名单:登出/封号时把 jti 写 Redis 黑名单,过滤器查一次(有网络开销,但即时)
方案 C 会话版本号:user 表存 token_version,JWT 里带 ver;改密时 ver+1,旧 token 全失效
组合推荐:A 为主,改密/封号场景叠加 C(改一个字段,全员旧 token 作废)
步骤 5:refresh 轮换在本站方案里的形态 登录:发 access JWT(15min) + refresh(不透明随机串, 7d, 存 Redis)
续期:POST /auth/refresh → 校验 Redis 中 refresh 有效 → 旋转(删旧发新)→ 新 access
重放检测:已作废的旧 refresh 再次出现 → 视为泄露 → 删该用户全部 refresh 强制重登
这正是第 6 课 OAuth rotation 的同一套纪律——自研方案不是「不用管安全」,而是把 OAuth 的安全机制自己实现一遍 。这也是为什么理解协议本身,对自研方案同样有价值。
步骤 6:什么时候该把密码轨升级成 OAuth 轨(决策卡) 出现以下任一需求,才引入 OAuth:
① 产品要求「用 GitHub/Google/微信登录」→ OAuth/OIDC 联邦轨
② 要对外开放 API 给第三方开发者 → Client Credentials / 授权码 + Resource Server
③ 公司内多系统要统一登录(SSO) → OIDC 企业 IdP
不满足却硬上 OAuth = 过度设计:多一套角色/端点/密钥管理,收益为零。
本站现状(自家前端 + 管理后台)→ 密码 + 自研 JWT 完全够用。
步骤 7:写给值班同学的排错口令 /admin 401 且从未走 GitHub → 查自研 JWT:cookie 带了吗?exp 过了吗?版本号对吗?
刚联邦成功却 /admin 401 → 查 success handler 是否签发了本站 JWT、前端是否写进 cookie
/api/v1 401 → 查是不是误用了自研 JWT 打 RS 链,或 IdP access 已过期
/api/v1 403 → token 有效但 scope 不够——不是「没登录」,别去跳登录页
讲清楚要点
JWT 描述编码,OAuth 描述协议 :比较「OAuth vs JWT」本身是错位比较;正确的对比是「自研登录方案 vs 标准授权协议」。
本站权限模型(角色、进度、会员)在本地库 :无论从哪条入口轨进来,最终都发本站 JWT——授权与撤销不受外部 IdP 牵制。
无状态是利也是弊 :扩展性换来撤销难题;补救组合(短寿命+refresh+黑名单/版本号)是面试必背、上线必配。
admin 轨不宜绑死 IdP :外部平台故障/风控会锁死你的管理入口;密码轨是应急生命线。
开放 API 用 OAuth scope 隔离 :第三方拿到的永远只是 scope 限定的能力,绝不能触达站内用户的会话凭证。
两条轨的日志要分开标记 (auth_kind=password|oauth|rs):排错先看路径与凭证种类,再看具体原因。
你可能会懵的地方
都叫 token,在 Network 里怎么分辨?
看发行方与路径:来自 /auth/login 的 JWT 只对站内接口有效;来自 IdP /token 的 access_token 只对 IdP API 或你的 RS 链有效。token 的效力由「谁能验它」决定,不由格式决定。
能不能只用 OAuth、废除密码?
ToC 产品可以;但管理后台建议保留密码轨作为应急通道。至少要保留一个不依赖外部 IdP 的本地管理员恢复手段。
Client Credentials 算登录吗?
不算。它是应用身份(机器对机器),没有终端用户、没有「登录态」,适合服务间调用;不要拿它当用户会话方案。
自研 JWT 算不算「实现了 OAuth」?
不算。OAuth 的核心是授权码流、四角色、scope、refresh 等协议约定;自研 JWT 只是「签发凭证 + 验证」的自家方案,没有协议角色与授权语义。
两者能共用一个过滤器吗?
不建议。用 securityMatcher 分两条链(第 8 课模式),每条只认一种凭证;「万能验证器」会把两类 token 的错误混在一起,排错噩梦。
错法对照 错法 问题 正解 把 IdP 的 access_token 当 admin 鉴权凭证 发行方/密钥都不对,且权限模型对不上 映射用户后签发本站 JWT 在密码登录链路里硬塞授权码概念 两种流程互相污染,实现与排错都乱 分轨分课:密码轨管站内,OAuth 管联邦 JWT 有效期设 7 天且无撤销手段 封号/改密后旧 token 依然横行 短寿命 + refresh + 版本号/黑名单 开放 API 与 admin 共用一把超级密钥 scope 与角色混用,越权风险大 两条链分区 + 各自的授权语言 以为学完 JWT 就等于会了 OAuth 缺协议角色、流程与安全机制认知 本 track 的流程课(2/3/4)补齐
卡点急救
现象:登录接口正常,但接口权限时灵时不灵 。多半是「多实例 + 每实例不同签名密钥」。统一密钥来源(环境变量/配置中心),或改用 RS256 公私钥分发。
现象:改密码后旧设备依然能操作 。JWT 无撤销的典型症状。立即上会话版本号:user 表加 token_version,改密时 +1,JWT 里带 ver,过滤器比对不一致即 401。
现象:既想接 GitHub 登录,又怕影响现有密码用户 。按第 7 课的分区模式接入:OAuth 链只 match /oauth2/**、/login/**;成功后发本站 JWT。上线前回归密码登录——双轨并存的关键就是「互不越界」。
面试常问
JWT 和 OAuth 是什么关系?
不同层面:JWT 是凭证编码格式(自包含、可验签),OAuth 是授权协议(四角色、授权码流、scope)。OAuth 的 access_token 常以 JWT 形态出现,但用 JWT 不代表实现了 OAuth——自研登录签发 JWT 就不是 OAuth。
JWT 无状态的缺点是什么?如何补救?
缺点:签发后无法主动作废,登出/封号/改密存在空窗。补救:① 短寿命 access + 可撤销 refresh(rotation);② Redis 黑名单记 jti;③ 会话版本号,改密即全员失效。生产常用「短寿命 + 版本号」组合,兼顾性能与即时性。
你们项目为什么自研 JWT 而不引入 OAuth?什么情况下会引入?
当前只有自家前端登录、无第三方接入与开放 API,OAuth 属于过度设计;自研 JWT 简单可控,且实现了短 access + refresh 轮换等同等安全机制。出现「第三方登录」「开放 API」「SSO」任一需求时再引入 OAuth,并沿用「联邦认人 → 发本站 JWT」的收尾模式。
自测三问
给三个场景各选一种方案:管理员后台登录、学员用 GitHub 登录、第三方开发者读课表。
本站自研 JWT 的撤销难题有哪三种补救?各有什么代价?
「OAuth vs JWT」这个说法误导在哪?更准确的说法是什么?
小结一句话 密码 + 自研 JWT 管站内会话,OAuth/OIDC 管联邦认人与对外授权——互补不替代;JWT 的无状态利弊与撤销补救,是两条轨共同的地基。
延伸