答案:用 echo.group 拆分路由,只对需要鉴权的组注册 jwt.middleware,而不是全局注册。这是最干净、最不易出错的方式;若必须混用,应自定义可跳过中间件并前置设置 c.set("skip_jwt", true),同时确保 options 预检请求被提前处理,避免 401。

如何在 Echo 中对特定路由跳过 JWT 中间件
直接答案:用 echo.Group 拆分路由,只对需要鉴权的组注册 jwt.Middleware,而不是全局注册。这是最干净、最不易出错的方式。
很多人误以为必须在中间件里写一堆 if path == "/login" || path == "/health" 做白名单判断,这既难维护又容易漏掉 OPTIONS 预检请求或带参数的路径(比如 /users/:id)。Echo 的设计本就支持按组隔离中间件,强行在中间件里做路径判断是反模式。
- 不要把
jwt.New()放在e.Use(...)里(即全局中间件) - 用
e.Group("")创建无前缀的公共组,放登录、注册、健康检查等接口 - 再建一个
authGroup := e.Group("/api"),把jwt.Middleware注册到它上面 - 所有需要鉴权的 handler 都挂在这个
authGroup下
// ✅ 正确做法
e := echo.New()
// 公共路由(不走 JWT)
public := e.Group("")
public.POST("/login", loginHandler)
public.GET("/health", healthHandler)
// 需要 JWT 的路由
auth := e.Group("/api")
auth.Use(jwt.Middleware([]byte("secret")))
auth.GET("/users", listUsersHandler)
auth.GET("/users/:id", getUserHandler)
如果必须复用同一组路由,怎么动态跳过验证
极少数场景下(比如遗留系统改造),你无法拆分路由组,只能在同一个 echo.Group 里混用鉴权/非鉴权接口。这时需自定义中间件,在 jwt.ExtractToken 后手动判断是否跳过校验。
注意:不能直接 return nil 或 return next(c),那样会跳过后续所有中间件(包括日志、CORS 等)。正确做法是设置一个上下文标记,让 JWT 验证逻辑主动跳过。
Echo框架 5.1.0 版本源码包下载,适合关注 RealIP 行为变化、StartConfig.Listener、NewDefaultFS 和观测性中间件入口的开发团队。
- 用
c.Set("skip_jwt", true)标记该请求无需验证 - 自定义中间件放在
jwt.Middleware之前,提前 set - JWT 中间件内部通过
c.Get("skip_jwt")判断是否直接调用next(c) - 官方
jwt.Middleware不支持跳过逻辑,必须自己封装一层
// 自定义可跳过的 JWT 中间件
func SkipableJWT(secret []byte) echo.MiddlewareFunc {
return func(next echo.Handler) echo.Handler {
return echo.HandlerFunc(func(c echo.Context) error {
if skip, _ := c.Get("skip_jwt").(bool); skip {
return next(c)
}
return jwt.FromAuthHeader()(next)(c)
})
}
}
// 使用
e := echo.New()
e.Use(skipableJWT([]byte("secret")))
e.GET("/health", func(c echo.Context) error {
c.Set("skip_jwt", true) // ⚠️ 必须在这行设置
return c.String(200, "OK")
})
OPTIONS 请求为什么总 401?怎么处理
前端发跨域请求时,浏览器会先发 OPTIONS 预检。如果你的 JWT 中间件注册在全局或大组上,而没单独处理 OPTIONS,它就会被拦截并返回 401 —— 因为预检请求不带 Authorization 头,JWT 解析必然失败。
- 绝对不要让
OPTIONS进入 JWT 验证流程 - 在 JWT 中间件前加一个预检处理中间件,对
OPTIONS直接返回 204 - 或者用
echo.WrapMiddleware包一层,过滤方法 - 确保 CORS 中间件(如
middleware.CORSWithConfig)注册在 JWT 之前
e.Pre(middleware.CORSWithConfig(middleware.CORSConfig{
AllowOrigins: []string{"*"},
AllowMethods: []string{echo.GET, echo.HEAD, echo.PUT, echo.PATCH, echo.POST, echo.DELETE},
}))
// 预检中间件必须在 JWT 之前
e.Use(func(next echo.Handler) echo.Handler {
return echo.HandlerFunc(func(c echo.Context) error {
if c.Request().Method == echo.OPTIONS {
return c.NoContent(http.StatusNoContent)
}
return next(c)
})
})
e.Use(jwt.Middleware([]byte("secret")))
为什么自定义 Claims 后验证总失败
跳过验证本身不难,但很多人在后续业务逻辑里仍调用 c.Get("user") 获取用户信息,结果得到 nil —— 因为 JWT 中间件根本没执行,自然不会往 context 里塞 "user"。
- 跳过 JWT 验证 ≠ 跳过用户信息初始化;这两件事是解耦的
- 如果某些接口(如
/login)需要生成 token,但本身不验证 token,那就别依赖c.Get("user") - 若需统一获取用户(不管是否鉴权),应自己实现一个
GetUserID(c echo.Context) (uint, bool)工具函数 - 注意:JWT 中间件默认把解析后的
*jwt.Token存到c.Get("user"),不是原始 claims
真正容易被忽略的是:跳过验证后,你的 handler 是否还隐式依赖了 user 上下文字段。检查所有用到 c.Get("user") 的地方,加空值判断,或者重构为显式传参。










