Skip to content

JWT 有哪些优缺点?注销登录和黑名单怎么实现? ​

  • 标签:Spring Security、认证授权、后端

🏛 业务架构师 ​

JWT 的优缺点与注销/黑名单方案:

优点:无状态(服务端不存会话,验签即认证——微服务/水平扩容友好)、自包含(用户与权限信息在令牌里,减少查库)、跨域跨服务传递方便(统一认证的标准货币)、标准协议生态成熟。

缺点:

  1. 无法主动作废(核心痛点):签发后到过期前始终有效——注销、封号、改权限不能"立即生效";
  2. Payload 可解码(非加密)——不能放敏感数据;
  3. 令牌体积比 Session ID 大(每请求带宽开销);
  4. 续签/注销需要额外机制补丁。

注销与黑名单的实现:

  1. 黑名单(Redis):注销时把该 JWT 的 jti(令牌唯一 ID)写入 Redis,TTL 设为令牌剩余有效期;网关/过滤器验签后再查一次黑名单——把"无状态"局部退化成"查一次缓存"(性能损耗极小,Redis 毫秒级)。这是主流方案;
  2. 短 Access + Refresh 撤销:注销只删 Refresh Token——Access 最长再活 15 分钟,业务可接受就选这个(连 Redis 都不用);
  3. 版本号/变更时间戳:用户表存 pwd_version 或 token_invalid_before 时间戳,JWT 里带签发时间,验签时比对——改密/封号全局立即生效,同样只需一次缓存/DB 查询(可加本地缓存抗热点);
  4. 全会话白名单(每令牌都查库)——安全性最高但丢掉无状态优势,一般不必。

我的架构建议:默认组合"短时效 Access + Refresh 可撤销 + Redis 黑名单兜底高权限场景"——每加一次查缓存是性能税,按业务风险分级征收:普通业务用 15 分钟自然过期即可,admin 后台的注销必须黑名单即时生效。安全设计从来是风险与成本的平衡术。

🏗 系统架构师 ​

JWT 的优缺点与注销/黑名单——架构师的核心认知:JWT 的优缺点是同一枚硬币(无状态):

优点:服务端无存储(验签即认证,水平扩展零会话同步);跨服务共享(微服务各自本地验签);跨域/移动端友好(不依赖 Cookie 域);标准生态(OIDC/网关/框架全支持)。

缺点:无法主动作废(签发到 exp 前始终有效——服务端"说了不算");payload 可读(不加密);体积比 session id 大(每请求传输);claims 更新难(改角色需重签);密钥管理是新的安全面。

注销与黑名单的实现(工程方案谱系):

  • 黑名单(吊销名单):注销时把 jti(或 token hash)写入 Redis(TTL = 该 token 剩余生命期);网关/服务验签后再查黑名单(O(1))。代价:引入一次远程查询——"无状态"优势打了折扣(但只在验签路径加一次 Redis 查,远轻于 session 全量存储,是主流折中);
  • 短 Access + Refresh 吊销:Access 15min 自然死亡,注销只吊 Refresh(服务端存储)——15 分钟内的残留访问是可接受窗口(敏感操作再校验);体验与无状态的平衡点,我推荐的默认方案;
  • 版本号/变更时间戳:用户表存 tokenVersion(或 pwdChangedAt),JWT 里带版本,改密码/踢人时 version++ 全端失效——按用户粒度全量作废的最简实现;
  • 网关统一收口:验签 + 黑名单只在网关做一次,下游透传用户信息(Header 注入)——避免每个服务都查 Redis;
  • 前端配合:清本地令牌只是体验层(不安全——令牌在攻击者手里仍有效),服务端吊销才是注销的本质。

选型:普通业务 = 短 Access + Refresh 吊销;强安全(封禁即时生效)= 加黑名单/版本号;没有完美方案,只有"可接受的失效窗口"与"查询成本"的权衡。

💻 全栈开发专家 ​

JWT 优点:无状态(服务端不存会话,水平扩容零成本)、自包含(用户/角色随 token 携带,少一次 DB 查询)、跨服务/跨域友好(微服务统一验签)、标准生态(OAuth2/OIDC 原生集成)。

缺点(每个都要有对策):

  1. 不可吊销(签发后到 exp 前一直有效)——登出/踢人/改密做不到「立即失效」;
  2. 不能更新内容(改角色要等旧 token 过期);
  3. payload 明文可解码——不能放敏感数据;
  4. 体积比 session id 大(每请求都带)。

注销与黑名单实现(把无状态「打补丁」成可吊销):

1. 黑名单方案(经典):

java
// 注销时:jti 进 Redis,TTL = token 剩余有效期
public void logout(String jwt) {
    Claims c = parse(jwt);
    redis.setex("jwt:black:" + c.getId(),
                c.getExpiration().getTime() - now(), "1");
}
// 网关/资源服务器验签后多查一步:
if (redis.hasKey("jwt:black:" + jti)) return unauthorized;
  • 优点:精准即时;缺点:每个请求多一次 Redis 查询——「无状态」打折(但 Redis 查询 ~1ms,比 session 存储轻得多,且可用本地布隆过滤器预筛)。

2. 版本号方案(我更推荐): 用户表加 token_version,签发时写入 claim;改密/注销 → version+1;验签时比对(可缓存 user version,改密才失效缓存)——一次改动吊销该用户全部历史 token,无需按 jti 追踪。

3. 短有效期兜底:access 15 分钟 + refresh 可撤销(refresh 存库可删)——黑名单只需覆盖 15 分钟窗口,压力极小。

选型口诀:短 access + 服务端 refresh + 版本号/黑名单二选一,无状态的安全代价就付清了。