Gin拦截必须显式调用c.Abort()并紧跟return,否则请求继续执行;真实IP解析须配置SetTrustedProxies,否则c.ClientIP()返回内网地址。

拦截器必须显式 abort 才算真正拦截
写了 c.JSON(403, gin.H{"msg": "forbidden"}) 却没跟 c.Abort() 和 return,等于白写。请求会继续往下走,后续中间件、甚至业务 handler 仍会执行——可能已改数据库、发短信、写日志。
-
c.Abort()是 Gin 中断中间件链的唯一方式,它清空 pending handlers 列表 - 必须紧跟
c.Abort()写return,否则函数继续执行(Go 不自动 return) - 常见错误写法:
c.JSON(401, ...); c.Abort()—— 缺少return,下一行仍会被执行 - 正确写法:
c.JSON(401, ...); c.Abort(); return
真实 IP 解析不配 SetTrustedProxies 就是裸奔
c.ClientIP() 在线上环境基本 useless,Nginx 或 CDN 转发后默认只信任 127.0.0.1,返回的全是内网地址(如 10.0.0.3 或 127.0.0.1)。
- 必须调用
engine.SetTrustedProxies([]string{"192.168.0.0/16", "203.208.60.0/24"})显式声明可信代理网段 - 若 Nginx 已配置
proxy_set_header X-Real-IP $remote_addr,优先读c.Request.Header.Get("X-Real-IP") - 多层代理(CDN → Nginx → Go)时,
X-Forwarded-For要取最左非信任地址,不是最右——攻击者可伪造尾部 IP - 不设
SetTrustedProxies就直接解析X-Forwarded-For,等于把用户可控头当真实 IP,黑白名单形同虚设
中间件注册位置决定作用域,别乱套用
全局 Use()、路由组 Group()、局部传参,三者行为差异极大,混用会导致拦截漏掉或过度触发。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
- 全局注册:
router.Use(AuthMiddleware())→ 所有路由(包括/health、/metrics)都会进该中间件 - 路由组注册:
admin := router.Group("/admin", AuthMiddleware())→ 仅匹配/admin/**的路径 - 局部注册:
router.GET("/debug", LogMiddleware(), func(c *gin.Context) {...})→ 仅该 endpoint 生效 - 注意:
router.StaticFS等静态资源路由默认不走中间件链,需显式包裹或改用router.Use(...).StaticFS(...)
洋葱模型里 Next() 不调用 = 断链,但 Abort() 后不能再调 Next()
Gin 的中间件是洋葱模型:前置逻辑 → c.Next() → 后置逻辑。漏掉 c.Next(),后续所有中间件和 handler 都不会执行;误在 c.Abort() 后再调 c.Next(),会 panic。
立即学习“go语言免费学习笔记(深入)”;
-
c.Next()是控制权移交的关键,不是可选;没它,请求就卡在当前中间件 -
c.Abort()后再调c.Next()会触发panic: next() called after abort() - 想实现“只执行前置、不走后置”,就只写前置逻辑 +
c.Abort(); return,不要碰c.Next() - 日志类中间件通常前后都写,鉴权类中间件通常前置判断 + 拒绝时
c.Abort(); return
SetTrustedProxies 配置和 Abort()+return 的组合动作——这两处出错,拦截逻辑就从防御变成摆设。

















