Buffalo中路由分组级中间件必须用app.Group而非app.Use,否则全局生效导致误拦截;正确做法是app.Group("/api/v1", func(g *buffalo.RouterGroup) { g.Use(AuthMiddleware) }),使鉴权仅作用于该前缀下所有路由。

路由分组级中间件注册必须用 app.Group,不是 app.Use
想只对 /api/v1 下所有接口做鉴权,却在全局调用 app.Use(AuthMiddleware),结果连 /public/logo.png 都被拦住——这是最常见误操作。Buffalo 的 app.Use 是全局注入,不区分路径前缀;真正控制作用域的是 app.Group。
正确写法是显式声明分组,并在闭包内注册中间件:
app.Group("/api/v1", func(g *buffalo.RouterGroup) {
g.Use(AuthMiddleware)
g.GET("/users", UsersHandler)
g.POST("/posts", PostsCreateHandler)
})
-
AuthMiddleware只会执行于该分组内所有路由,/health、/public/完全不受影响 - 分组可嵌套:比如
app.Group("/admin", ...).Group("/users", ...),中间件按嵌套层级叠加 - 注意:分组内注册的路由不会自动继承父分组中间件,必须显式调用
g.Use()
AuthMiddleware 里怎么安全读取用户身份?别碰 c.Session().Get("user_id") 就完事
很多实现直接从 session 拿 user_id,但没验证 session 是否有效、是否过期、签名是否被篡改。更糟的是,如果用了 JWT 认证,session 根本不存用户 ID,而是靠 header 里的 Authorization: Bearer xxx 解析。
实操建议分三步走:
- 先检查
c.Request().Header.Get("Authorization")是否存在且以"Bearer "开头 - 用
jwt.Parse(或你选的库)解 token,校验exp、iss、签名,失败就c.Error(401, ...) - 解析成功后,把用户 ID 和角色写入 context:
c.Set("user_id", uid)、c.Set("role", role),后续 handler 直接用c.Param("user_id")或c.Value("user_id")读取
别在中间件里查 DB 拿用户详情——那是 handler 的事;中间件只做身份核验和上下文注入。
部分接口豁免鉴权,用 .Use 链路短路最稳
比如 /api/v1/login 和 /api/v1/public/stats 必须绕过鉴权,但又不想拆出新分组。这时候不能靠“if path != /login”,因为中间件顺序一变就失效;应该用链路短路。
开箱即用的技能链路由引擎。13 条预定义链覆盖搜索、开发、审查、MLOps、法律、创意等场景,三层路由架构(触发词→SAD反馈→DAG编排),recall@10=96.97%。配置驱动(chains.yaml),零代码扩展。pip install skill-weave-chains 一键安装。
在鉴权中间件开头加判断:
func AuthMiddleware(next buffalo.Handler) buffalo.Handler {
return func(c buffalo.Context) error {
path := c.Request().URL.Path
method := c.Request().Method
if (path == "/api/v1/login" && method == "POST") ||
(path == "/api/v1/public/stats" && method == "GET") {
return next(c) // 直接放行,不执行鉴权逻辑
}
// 正常鉴权流程...
return next(c)
}
}
- 这种写法比在路由注册时漏掉
.Use()更可靠,避免漏加或顺序错乱 - 条件判断尽量精简,别查 DB、别调外部 API,否则豁免接口也变慢
- 别用正则匹配路径——
/api/v1/.*看似方便,但容易误伤/api/v1/login/xxx这类非法路径
权限粒度到接口级别,c.Request().Method + Path 映射权限字符串
RBAC 不是只分 “admin” 和 “user”,而是每个接口对应一个权限标识,比如 POST /api/v1/posts → "post:create"、DELETE /api/v1/users/:id → "user:delete"。中间件里硬编码 if 判断会失控,必须查表。
关键点在于缓存权限集:
- 在鉴权中间件里,从
c.Value("user_id")拿 ID,查 Pop 关联出所有permissions字符串 - 转成
map[string]bool存进 context:c.Set("perms", permsMap) - 后续 handler 或其他中间件(如日志)都能复用,不用重复查 DB
- 映射规则写死在代码里即可,例如
permMap := map[string]string{"POST /api/v1/posts": "post:create", ...}
真正难的是权限变更后如何刷新缓存——它不像 session 能设 TTL,目前 Buffalo 没内置机制,得靠手动清 Redis 或重启服务,这点容易被忽略。










