JWT标准校验(签名、exp等)不依赖外部存储,数据库或Redis仅用于黑名单管理;必须通过jti字段查Redis判断是否登出,且该中间件须在echo-jwt解析后执行。

不能直接“结合数据库验证JWT是否有效”——JWT本身是无状态的,数据库查的不是token有效性,而是它是否被主动注销(即黑名单)。 你真正要做的,是在 echo-jwt/v4 解析签名和时间字段之后,额外加一层「查数据库/Redis判断是否已被登出」的逻辑。
为什么不能让数据库参与JWT基础校验
JWT的标准验证流程(签名、exp、nbf、iat)完全由密钥和时间戳决定,不依赖任何外部存储。一旦你把数据库查询塞进 KeyFunc 或 AuthMiddleware 的解析阶段:
– 性能暴跌:每个请求都查一次DB,失去无状态优势
– 单点故障:DB挂了,所有API 500
– 时序错乱:token已过期但DB里还没删,或刚登出DB写入延迟导致漏检
所以,数据库(或更推荐 Redis)只该用于管理「已签发但需提前作废」的 token,也就是黑名单。
查黑名单必须用 jti 字段,别用 token.Raw
从 *jwt.Token 中安全提取唯一标识,靠的是 claims["jti"],不是 token.Raw(后者是完整字符串,不可作为key存库)。否则:
– 多次刷新 token 会生成不同 Raw,但同一 jti 可确保登出一次全失效
– 没设 jti?签发时就得补:claims["jti"] = uuid.NewString()
– 查库时 key 格式统一为 revoked:{jti},TTL 设为和 token exp 对齐(比如 15 分钟)
– Redis 推荐用 SET revoked:{jti} "" EX 900 NX 写入,避免并发重复登出覆盖 TTL
黑名单中间件必须放在 echo-jwt 之后,且禁止重解析
你不需要、也不应该在自定义中间件里调 jwt.Parse——echo-jwt 已把解析好的 *jwt.Token 放进 c.Get("user"):
– 类型断言获取:token, ok := c.Get("user").(*jwt.Token)
– 提取 jti:jti, ok := token.Claims.(jwt.MapClaims)["jti"].(string)
– 查 Redis:val, err := rdb.Get(ctx, "revoked:"+jti).Result()
– 若 err == redis.Nil 表示未登出,放行;否则 return echo.NewHTTPError(http.StatusUnauthorized, "token revoked")
– 切记:这个中间件必须注册在 e.Use(echojwt.WithConfig(...)) 之后,顺序错了就拿不到 "user"
数据库只是备选,Redis 才是生产首选
如果非要用数据库(比如 PostgreSQL)存黑名单:
– 建表必须带 jti 索引和 created_at 字段
– 查询语句要走 WHERE jti = $1 AND created_at > NOW() - INTERVAL '15 minutes',否则历史垃圾数据越积越多
– 但 DB 的写入延迟、连接池竞争、慢查询风险远高于 Redis 的原子操作
– 更关键的是:你无法用 DB 实现 NX 这种防并发覆盖的语义,登出瞬间可能漏掉请求
所以,除非合规强要求审计日志落盘,否则黑名单一律走 Redis,数据库只管用户主数据。


















