应使用e.group("/api")创建路由组并调用use(jwt.jwt(...))挂载jwt中间件,确保仅该组内路由受保护;/login等无需鉴权接口应置于独立的public组,避免全局注册后条件跳过。

JWT中间件怎么加到特定路由组
直接用 echo.Group 划分受保护的路由,再在该组上挂载 JWT 中间件,是最干净的做法。别把中间件全局注册后靠 if 判断路径跳过——容易漏、难维护、还影响性能。
常见错误是把 jwt.JWT([]byte("secret")) 直接传给 e.Use(),结果整个服务所有接口都强制鉴权了。
- 创建子路由组:
auth := e.Group("/api") - 只对这个组启用 JWT:
auth.Use(jwt.JWT([]byte("your-secret-key"))) - 组内定义的路由(如
auth.GET("/user", handler))才受保护;e.GET("/health", handler)这类根路由完全不受影响
解析出来的用户信息怎么安全拿到
JWT 中间件验证通过后,会把解析出的 token 存进 echo.Context 的 echo.JwtToken key 里,不是直接塞进 context.Get("user") —— 那是旧版或自定义中间件的习惯,官方 jwt 包不这么干。
正确做法是调用 c.Get("user") 吗?不是。它返回的是 *jwt.Token 实例,需要手动解析 payload:
token := c.Get("user").(*jwt.Token)
claims := token.Claims.(jwt.MapClaims)
userID := claims["user_id"].(float64) // 注意:JSON number 默认转成 float64
更稳妥的方式是用类型断言 + 检查,避免 panic:
- 先确认
c.Get("user") != nil - 再断言为
*jwt.Token,检查token.Valid - 最后从
claims取字段时,用claims["sub"]这种方式,别硬转float64—— 如果你存的是字符串 ID,就该在签发时统一用字符串
401 错误返回空响应?怎么自定义提示
默认 JWT 中间件遇到无效 token 或签名失败,直接写 401 并返回空 body。前端很难区分是 token 过期、格式错误,还是密钥不匹配。
开箱即用的技能链路由引擎。13 条预定义链覆盖搜索、开发、审查、MLOps、法律、创意等场景,三层路由架构(触发词→SAD反馈→DAG编排),recall@10=96.97%。配置驱动(chains.yaml),零代码扩展。pip install skill-weave-chains 一键安装。
解决方法是传入自定义的 jwt.Config.ErrorHandler:
config := jwt.Config{
SigningKey: []byte("your-secret-key"),
ErrorHandler: func(c echo.Context, err error) error {
return c.JSON(http.StatusUnauthorized, map[string]string{
"error": "invalid or expired token",
})
},
}
auth.Use(jwt.JWTWithConfig(config))
注意点:
-
ErrorHandler必须返回error,否则 Echo 不会终止后续处理 - 不要在里面调用
c.String()或c.JSON()后再 return nil —— 这会导致重复写 response header - 如果想根据
err类型细分错误(比如jwt.ErrExpired),可以用errors.Is(err, jwt.ErrExpired)
怎么让 /login 路由跳过 JWT 验证但其他 /api/xxx 都要验
这是最常踩的坑:以为只要不把 /login 写进 auth.Group 就安全了,结果发现它根本没被任何中间件管,连基本的 content-type 校验、body 限制都没生效。
正确结构是分层设计:
- 根路由
e上挂通用中间件(如Logger、Recover、BodyLimit) - 单独建一个
public := e.Group("/api")放/login、/register等无需鉴权的接口 - 再建一个
protected := e.Group("/api"),并立即调用protected.Use(jwt.JWT(...)) - 两个 group 路径可以一样,Echo 允许同前缀多 group,匹配按注册顺序来 —— 所以
public.POST("/login")和protected.GET("/user")完全互不干扰
别试图在一个 group 里用中间件“排除”某个路由——Echo 没提供 skip 机制,硬绕只会让逻辑变脆弱。










