私有路由必须挂载在带jwt中间件的echo.group下,而非根路由;正确做法是创建独立分组(如auth := e.group("/api")),在其上调用auth.use()注册jwt中间件,并将私有接口挂载于此,确保鉴权生效且避免误拦截/public等免鉴权路径。

私有路由必须挂载在带 JWT 中间件的 echo.Group 下
直接把 handler 挂到 e.GET("/user", h) 这种根路由上,哪怕写了 e.Use(jwt.Middleware(...)),也大概率不生效——因为 e.Use() 是全局中间件,但它的执行依赖于路由匹配后才触发,而 JWT 中间件本身需要先解析并校验 token 才能放行;若中间件注册顺序错、或路径没被它覆盖,c.Get("user") 就是空的。
正确做法是显式创建一个私有路由组,并只在这个组里注册 JWT 中间件:
e := echo.New()
auth := e.Group("/api")
auth.Use(echojwt.WithConfig(echojwt.Config{
SigningKey: []byte("your-secret"),
ContextKey: "user",
}))
auth.GET("/profile", profileHandler)
auth.POST("/order", createOrderHandler)
-
/api是物理隔离的前缀,所有挂在此组下的路由天然受保护 - 不要把
/login或/health误配进这个组,否则它们也会被强制校验 - 中间件必须用
auth.Use()而不是e.Use(),否则会作用于全部路由(包括不该鉴权的)
echojwt.Config 必须设对 SigningKey 和 ContextKey
常见 panic 来自两个地方:一是 SigningKey 类型不对,二是后续 handler 用错了 key 去取用户信息。
SigningKey 必须是 []byte,传 "secret" 字符串会编译失败;ContextKey 默认是 "user",但如果你改成了 "jwt_user",那 handler 里就得写 c.Get("jwt_user"),否则取出来是 nil。
开箱即用的技能链路由引擎。13 条预定义链覆盖搜索、开发、审查、MLOps、法律、创意等场景,三层路由架构(触发词→SAD反馈→DAG编排),recall@10=96.97%。配置驱动(chains.yaml),零代码扩展。pip install skill-weave-chains 一键安装。
- 静态密钥:用
SigningKey: []byte(os.Getenv("JWT_SECRET")),别漏掉[]byte()强转 - 动态密钥(如按用户查):必须用
KeyFunc,且里面要校验token.Header["alg"],防alg:none攻击 - 务必检查
token.Valid == true,仅判err == nil不够——过期 token 的err是nil,但token.Valid是false
handler 里安全取用户信息要分三步
很多人在 handler 开头直接写 user := c.Get("user").(*jwt.Token),一遇到未认证请求就 panic。真正安全的取法是:
if user, ok := c.Get("user").(*jwt.Token); !ok || !user.Valid {
return c.JSON(401, map[string]string{"error": "unauthorized"})
}
if claims, ok := user.Claims.(jwt.MapClaims); !ok {
return c.JSON(401, map[string]string{"error": "invalid claims"})
}
userID := uint64(claims["user_id"].(float64))
- 先断言类型,再检查
Valid,顺序不能反 -
claims["user_id"]是float64(JSON number),不是int,强转要小心 - 如果签发时用了自定义结构体(如
CustomClaims),这里必须对应断言为该类型,不能硬转jwt.MapClaims
OPTIONS 预检请求容易被 401 拦截
CORS 预检请求(OPTIONS)不带 Authorization 头,但若它命中了带 JWT 的路由组,就会被中间件拦下返回 401,导致前端 AJAX 请求直接失败。
解决方法不是关 CORS,而是让预检请求提前放行:
- 在注册 JWT 中间件前,加一个前置中间件,对
OPTIONS方法直接return next(c) - 或者用
Skipper函数跳过OPTIONS:Skipper: func(c echo.Context) bool { return c.Request().Method == "OPTIONS" } - 别依赖前端“自动带 header”,确保 API 文档明确写清哪些路径要处理预检
os.Getenv("JWT_SECRET") 直接读字符串,密钥应通过 secret manager 注入;同时注意服务器时间是否偏差超过 token 的 nbf/exp 容忍窗口。










