flask无原生中间件,核心机制是before_request钩子:在请求进入视图前执行ip校验;需正确提取真实ip(如优先cf-connecting-ip、再x-forwarded-for首非私有ip、最后fallback remote_addr),并用ipaddress模块精确匹配cidr网段,匹配失败返回403。

Flask中间件拦截IP的核心机制是啥
Flask本身没有“中间件”概念(不像Django或FastAPI有明确的middleware类),但可以通过before_request钩子函数实现等效逻辑——它在每次请求进入视图前执行,适合做IP校验。关键点在于:IP地址必须从request.remote_addr获取,但要注意反向代理(如Nginx)场景下该值可能被污染,需结合X-Forwarded-For头处理。
常见错误现象包括:
- 本地测试时
request.remote_addr始终是127.0.0.1,误以为拦截失效 - 部署到Nginx后所有请求IP变成
127.0.0.1,实际IP丢失 - 用
request.headers.get('X-Forwarded-For')直接取值,未做分割和清洗,导致校验失败
如何正确提取客户端真实IP
真实IP提取必须分场景判断,不能无脑信任某个字段:
使用场景决定取值策略:
- 开发环境(无代理):
request.remote_addr可直接用 - 生产环境(Nginx/Cloudflare等代理):
X-Forwarded-For头通常为逗号分隔字符串,取第一个非私有IP(如192.168.x.x、10.x.x.x需跳过) - Cloudflare用户:应优先读
CF-Connecting-IP头,比X-Forwarded-For更可靠
def get_client_ip():
if request.headers.get('CF-Connecting-IP'):
return request.headers.get('CF-Connecting-IP')
xff = request.headers.get('X-Forwarded-For', '').split(',')[0].strip()
if xff and not ipaddress.ip_address(xff).is_private:
return xff
return request.remote_addr
注意:必须导入ipaddress模块,且is_private能自动识别127.0.0.0/8、10.0.0.0/8等标准私有网段。
SkillSub Pro - Python 题解与代码注释双功能技能功能概述SkillSub Pro - Python 题解与代码注释双功能技能是一项面向实际任务的技能,主要用于SkillSub Pro 是一个 Python 题解生成与代码注释的 双功能合体技能 ,专为学生、算法学习者和开发者设计;✅ 一个技能,两种用途 :;核心要点📝 题解模式 :输入题目/题号,自动生成完整 Python 题解(含详细注释、解题思路、复杂度分析);💬 注释模式 :输入 Python 代码,自动添加详细中。它将相关步骤、
用ipaddress模块匹配非法IP网段最稳妥
硬编码字符串匹配(如ip.startswith('192.168.'))极易出错,应使用ipaddress标准库:
参数差异影响大:
-
ipaddress.ip_network('192.168.0.0/16')支持CIDR,也支持'192.168.0.0/255.255.0.0' -
ipaddress.ip_address(ip) in network是唯一推荐的包含判断方式 - 不要用
str(ip) in str(network),这是典型错误
import ipaddress
<p>BANNED_NETWORKS = [
ipaddress.ip_network('192.168.0.0/16'),
ipaddress.ip_network('10.0.0.0/8'),
ipaddress.ip_network('172.16.0.0/12'),
]</p><p>@bp.before_request
def block_illegal_ips():
client_ip = get_client_ip()
try:
ip_obj = ipaddress.ip_address(client_ip)
for net in BANNED_NETWORKS:
if ip_obj in net:
abort(403, 'Access denied from private network')
except ValueError:
pass # 非法IP格式,不拦截,由后续逻辑处理
</p>
为什么403响应后还要注意日志和性能
拦截本身开销极小,但两个细节容易被忽略:
性能与兼容性影响:
- 每次请求都调用
ipaddress.ip_address()和多次in判断,对高频访问服务有微小压力;可提前将BANNED_NETWORKS转为set或预编译,但一般无需优化 - 未记录拦截日志,导致安全审计缺失;建议在
abort前加app.logger.warning(f'Blocked IP {client_ip}') - 某些CDN会把自身IP(如Cloudflare的
173.245.48.0/20)塞进X-Forwarded-For末尾,若只取第一个字段,可能误拦合法用户
真实复杂点在于:你永远不知道前端到底有多少层代理,以及它们怎么篡改头信息。所以get_client_ip()函数必须按可信度降序检查多个来源,并始终 fallback 到remote_addr。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!










