echo-jwt/v4 默认不校验 exp/nbf/iat 且允许 alg: none,极不安全;须用 parsewithclaims + standardclaims + 显式 token.valid 检查,并缓存 claims 到 context;refresh_token 需独立实现状态化管理。

直接用 labstack/echo-jwt/v4 默认配置,基本跑不通生产环境——它默认不校验 exp、nbf、iat,也不拒绝 alg: none,token 过期了照样能进接口。
为什么 echo-jwt/v4 的默认 Parse 不安全
默认的 jwt.Parse 行为太宽松:它只校验签名格式和密钥匹配,但不会主动检查时间字段是否过期、是否未生效,更不会拦截 none 算法攻击。这意味着你传一个伪造的、exp: 9999999999 的 token,只要签名“看起来对”,中间件就放行。
必须显式启用验证逻辑:
- 用
jwt.ParseWithClaims(tokenString, &MyClaims{}, keyFunc)替代裸jwt.Parse -
keyFunc中要根据token.Header["alg"]返回密钥,并对"none"直接返回错误 -
MyClaims必须嵌入jwt.StandardClaims,以便调用Valid() - 解析后必须判断
if !token.Valid { ... },不能只看err == nil
如何在 Echo 中正确注入 claims 并复用
常见错误是每次请求都重新解析 token,或只把原始字符串塞进 c.Set("user", tokenString),导致后续 handler 只能再 parse 一遍。这浪费 CPU,也容易漏掉时间校验。
Echo框架 5.1.0 版本源码包下载,适合关注 RealIP 行为变化、StartConfig.Listener、NewDefaultFS 和观测性中间件入口的开发团队。
推荐做法是:在中间件里完成完整解析 + 校验后,把结构化 claims 缓存到 context:
- 定义
type MyClaims struct{ UserID string `json:"user_id"`; jwt.StandardClaims } - 解析成功且
token.Valid为 true 后,执行c.Set("claims", claims) - 后续 handler 中用
c.Get("claims").(*MyClaims)直接取用,无需再 parse - 避免在多个中间件里重复解析:加个
if c.Get("claims") != nil提前 return
access_token + refresh_token 的无状态假象与实操约束
JWT 本身无状态,但 refresh_token 流程必然引入状态——你得拒绝已被撤销、被盗用或不属于当前设备的 refresh 请求。
所谓“无状态扩展”在这里其实是妥协设计:
-
access_token设短有效期(如15 * time.Minute),只含user_id和角色,绝不塞密码、手机号等敏感字段 -
refresh_token不走 JWT,而是生成随机字符串(如uuid.NewString()),哈希后存 Redis,key 为refresh:{user_id}:{fingerprint} - 登录响应中用
Set-Cookie返回 refresh_token,带HttpOnly、Secure、SameSite=Strict - refresh 接口收到请求后,先查 Redis 是否存在且匹配设备指纹,再签发新 access_token
最易被忽略的一点:echo-jwt 中间件本身不处理 refresh 流程,它只管 access_token 验证;refresh 是独立路由+业务逻辑,必须自己实现存储、比对、吊销,否则“无状态”就退化成“无防护”。










