Echo中间件通过RealIP在请求进入路由前拦截非法IP,需配置IPExtractor和TrustProxyFunc以正确获取真实IP,白名单用net.Contains匹配CIDR,黑名单逻辑取反,返回403即中断流程。

怎么用 Echo 中间件拦截非法 IP
直接在请求进入路由前做 IP 判断,是 Echo 实现黑白名单最轻量、最可控的方式。中间件返回 echo.HTTPError 或调用 c.NoContent(403) 即可中断后续处理。
关键不是“怎么写逻辑”,而是“在哪插、怎么取 IP、取哪个 IP”。很多线上问题其实出在 c.RealIP() 没配对或被代理头污染。
- 默认
c.IP()返回的是直连客户端 IP(如本地测试可用),但上生产几乎总是反向代理(Nginx / ALB / CDN),必须依赖X-Forwarded-For或X-Real-IP - 务必提前调用
e.IPExtractor = echo.ExtractIPFromXFFHeader(或自定义提取器),否则c.RealIP()仍返回远端地址 - 如果用了 Cloudflare,要额外信任其边缘 IP 段,并设置
e.TrustProxyFunc,否则所有请求都会变成100.64.0.0/10类似地址
白名单中间件:只放行指定 IP 段
白名单逻辑简单:不在列表里就拒掉。但要注意 CIDR 匹配不能靠字符串包含,得用标准库 net.Contains()。
func IPWhitelist(ips ...string) echo.MiddlewareFunc {
var ipNets []*net.IPNet
for _, ip := range ips {
if _, net, err := net.ParseCIDR(ip); err == nil {
ipNets = append(ipNets, net)
} else if ipAddr := net.ParseIP(ip); ipAddr != nil {
// 单 IP 转成 /32
_, net, _ := net.ParseCIDR(ipAddr.String() + "/32")
ipNets = append(ipNets, net)
}
}
return func(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
realIP := c.RealIP()
if realIP == "" {
return echo.NewHTTPError(403, "missing client IP")
}
ip := net.ParseIP(realIP)
if ip == nil {
return echo.NewHTTPError(403, "invalid client IP")
}
allowed := false
for _, net := range ipNets {
if net.Contains(ip) {
allowed = true
break
}
}
if !allowed {
return echo.NewHTTPError(403, "access denied by whitelist")
}
return next(c)
}
}
}
使用时:e.Use(IPWhitelist("192.168.1.0/24", "2001:db8::/32"))
黑名单中间件:主动屏蔽恶意 IP
黑名单和白名单结构一致,只是判断逻辑取反。但实际部署中,黑名单更常配合限流或动态更新(比如从 Redis 读取实时封禁列表)。
Echo框架 5.1.0 版本源码包下载,适合关注 RealIP 行为变化、StartConfig.Listener、NewDefaultFS 和观测性中间件入口的开发团队。
硬编码黑名单适合临时应急;长期运行建议把 ipNets 换成带锁的 sync.Map 或封装为可热更新的结构体。
- 注意顺序:黑名单中间件应放在限流、认证等中间件之前,避免恶意请求打穿前置逻辑
- 若需记录拦截日志,别用
fmt.Println,改用c.Logger().Warn(),否则会丢失请求上下文 - 不要在中间件里做阻塞 IO(如每次请求都查一次数据库),这是高频路径
为什么 RealIP 经常拿错?
根本原因:Echo 默认不信任任何代理头,c.RealIP() 本质是 c.IP() 的别名,除非你显式配置了 IP 提取策略和可信代理段。
典型错误配置:
- 只设了
e.IPExtractor = echo.ExtractIPFromXFFHeader,但没设e.TrustProxyFunc→ 所有X-Forwarded-For都被忽略 - 反向代理没传
X-Forwarded-For(Nginx 忘加proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;) - Cloudflare 环境下,没调用
e.TrustProxyFunc = func(remoteIP string) bool { return cloudflare.IsCloudflareIP(remoteIP) }
验证方式:打印 c.Request().Header.Get("X-Forwarded-For") 和 c.RealIP() 对比,再检查 c.Request().RemoteAddr 是否含端口(如 127.0.0.1:54321)——如果是,说明没走代理,RealIP 就该等于它。

















