在 Beego 中注册自定义中间件必须调用 beego.InsertFilter,且须在 beego.Run() 前执行;常用类型为 beego.BeforeRouter,静态资源需用 beego.BeforeStatic;不注册则中间件不生效。

如何在 Beego 中注册并启用自定义中间件
Beego 的中间件必须在 main.go 中通过 beego.InsertFilter 注册,且顺序决定执行时机——越早插入的中间件越先执行。不注册就等于没写。
常见错误是把中间件函数直接传给 beego.Router 或误放在 init() 之外的包级位置,导致运行时完全不触发。
- 必须在
beego.Run()之前调用beego.InsertFilter - 推荐使用
beego.BeeApp.Handlers之外的方式,避免干扰默认路由逻辑 - 过滤器类型选
beego.BEFORE_ROUTER(最常用),确保在路由匹配前拦截请求 - 若需对静态资源也生效,不能用
beego.BEFORE_STATIC——它只对/static/路径有效,且不支持 IP 判断上下文
IP 获取逻辑为什么不能只读 r.RemoteAddr
r.RemoteAddr 返回的是直接 TCP 连接的地址,当服务部署在 Nginx、SLB 或 Cloudflare 后,它永远是反向代理的内网 IP(如 127.0.0.1:54321),不是真实客户端 IP。
正确做法是按优先级依次检查 HTTP 头:X-Forwarded-For → X-Real-IP → 回退 r.RemoteAddr,并做基础校验(防伪造、去端口、IPv4/IPv6 标准化)。
-
X-Forwarded-For是逗号分隔列表,取第一个非私有地址(如排除10.0.0.0/8、127.0.0.1、::1) - 务必调用
net.ParseIP()并用ip.IsGlobalUnicast()判断是否为公网可路由地址 - 忽略
X-Forwarded-For中含多个 IP 且首 IP 是内网地址的情况,否则容易被绕过
黑白名单存储结构与匹配性能怎么平衡
白名单和黑名单本质是“IP 是否存在”的判断问题。用 map[string]struct{} 做内存缓存是最快方式,但要注意 CIDR 网段匹配不能靠字符串相等——必须用 net.IPNet.Contains()。
常见错误是把 192.168.1.0/24 拆成 256 个字符串塞进 map,既浪费内存又无法动态更新网段。
- 预解析所有配置中的 CIDR 字符串为
*net.IPNet,存入切片;纯 IP 存入map[string]struct{} - 匹配时先查 map(O(1)),再遍历 CIDR 切片(O(n),但 n 通常
- 避免在每次请求中重复调用
net.ParseCIDR(),应在初始化阶段完成 - 若需热更新,用
sync.RWMutex保护两个数据结构,读多写少场景下性能影响极小
如何让中间件支持配置驱动且不硬编码规则
把 IP 规则写死在代码里等于放弃运维能力。Beego 支持从 app.conf 读取配置,但原生不支持数组或复杂结构,需手动解析。
推荐格式:用英文逗号分隔 IP 和 CIDR,例如 ip_whitelist = "127.0.0.1,192.168.0.0/16,2001:db8::/32",并在初始化时拆分、校验、加载。
- 配置项命名统一加前缀,如
filter.ip_blacklist,避免和其他模块冲突 - 解析失败时记录
beego.Error()并 panic,防止静默失效 - 空配置(如
ip_whitelist = "")应视作“无白名单”,而非拒绝全部请求 - 若需区分环境(dev/staging/prod),用 Beego 的
beego.AppConfig.String("ip_whitelist::" + beego.AppConfig.String("runmode"))
真正难的不是写一个能跑的中间件,而是想清楚谁在什么环节可能伪造 IP、网段重叠时以谁为准、配置加载失败是否该让整个服务起不来——这些边界情况一旦漏掉,黑白名单就只剩心理安慰作用。


















