echo框架v5起已移除内置jwt中间件,必须使用github.com/labstack/echo-jwt/v4独立包,配合github.com/golang-jwt/jwt/v5签发验证;需手动提取bearer token、显式校验token.valid及exp等字段,并通过c.set("user", user)注入用户信息。

JWT认证在Echo中要自己写中间件
Echo本身不内置JWT支持,必须手动实现中间件来解析、验证和注入用户信息。官方推荐方式是用 github.com/golang-jwt/jwt/v5(v5版)配合 echo.MiddlewareFunc 构建校验逻辑,而不是依赖过时的 jwt-go(v3/v4)——后者有已知的 KeyFunc 旁路漏洞,且不兼容Go 1.21+的泛型约束。
常见错误现象:请求头带 Authorization: Bearer xxx,但中间件直接 panic 或返回 401 却没说明原因;或 token 过期了却仍被接受(通常因未校验 exp 字段或时钟偏差未处理)。
- 从
echo.Context.Request().Header.Get("Authorization")提取 token,需手动切掉"Bearer "前缀 - 用
jwt.ParseWithClaims解析,传入自定义jwt.MapClaims或结构体,**必须显式检查err == nil && token.Valid** ——仅判 err 不够,因为签名正确但过期时 err 为 nil、token.Valid 为 false - 校验通过后,把用户数据存进
c.Set("user", user),后续 handler 用c.Get("user")取,别塞进 context.Value(Echo 的 c.Set 是安全的)
如何安全地管理密钥和签发Token
JWT 签名密钥不能硬编码或用 os.Getenv 直接读字符串——若环境变量被日志打印或调试暴露,整个认证体系就崩了。生产环境应使用 crypto/rand 生成 32 字节以上随机密钥,并通过 secret manager(如 HashiCorp Vault)注入。
签发时注意三点:一是用 jwt.SigningMethodHS256 而非 HS384/512(除非有合规要求),性能差异明显;二是设置 exp 必须用 time.Now().Add(...).Unix(),别手算秒数;三是避免把敏感字段(如密码哈希、权限列表)全塞进 payload,只放必要标识(如 user_id, role)。
- 签发示例:
token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{"user_id": 123, "exp": time.Now().Add(24*time.Hour).Unix()}) - 签名时用
token.SignedString(key),key 类型必须是[]byte,不是 string - 如果要用 RSA,私钥只用于签发(服务端),公钥用于校验(同样需预加载,别每次读文件)
为什么 /login 接口不能走 JWT 中间件
/login 是颁发 token 的入口,它本身不能被 JWT 中间件保护——否则请求根本进不来。典型错误是把所有路由统一加 jwtMiddleware,结果登录接口返回 401,卡死整个流程。
Echo框架 5.1.0 版本源码包下载,适合关注 RealIP 行为变化、StartConfig.Listener、NewDefaultFS 和观测性中间件入口的开发团队。
正确做法是显式分组:auth := e.Group("/auth") 下挂 auth.POST("/login", loginHandler)(无中间件),再建 api := e.Group("/api") 并挂 api.Use(jwtMiddleware),后续所有 /api/xxx 才受保护。
- 不要在 login handler 里调用
c.Get("user")——此时还没 set,会是 nil - login 成功后,用
c.JSON(200, map[string]string{"token": signedToken})返回,前端自行存 localStorage 或 httpOnly cookie - 若前端存 cookie,后端签发时得配
http.Cookie{HttpOnly: true, Secure: true, SameSite: http.SameSiteStrictMode},并让 JWT 中间件从 cookie 读(而非 header)
Refresh Token 怎么和 Echo 配合做
Echo 不管 refresh 流程,但容易踩坑的是:refresh endpoint 本身需要认证(用旧 refresh token),但它和 access token 的校验逻辑不同——比如 refresh token 应该有更长有效期、绑定设备指纹、且用独立密钥签名。很多人直接复用同一套 JWT 校验中间件,导致 refresh token 也能访问业务接口。
解决方法是拆两个中间件:accessJWT 和 refreshJWT,分别用不同密钥和 claims 结构。refresh handler 验证通过后,**必须废弃旧 refresh token**(例如存 Redis 记录黑名单,或用数据库标记失效),再签发新 pair(新的 access + 新的 refresh)。
- refresh 请求头通常是
Authorization: Bearer <refresh_token></refresh_token>,但语义上它不是 access token,中间件里要区分处理 - access token 的
exp设 15–60 分钟,refresh token 的exp设 7 天,但实际有效期靠黑名单控制 - 别把 refresh token 放 localStorage —— XSS 可窃取;应设 httpOnly cookie,并确保前端调 refresh 时自动携带
最常被忽略的是时钟同步问题:服务器时间比 NTP 慢几分钟,会导致刚签发的 token 被判为“尚未生效”(nbf 校验失败)。上线前务必用 ntpd -q 或 chronyd 校准系统时间。










