refactor: 简化 IP 获取逻辑并将请求体超时配置化

- 移除 TRUSTED_PROXY_COUNT 配置,改为优先使用 X-Real-IP 头
- 添加 REQUEST_BODY_TIMEOUT 环境变量,默认 60 秒
- 统一 get_client_ip 逻辑,优先级:X-Real-IP > X-Forwarded-For > 直连 IP
This commit is contained in:
fawney19
2026-01-06 16:29:03 +08:00
parent 28209e1c2a
commit 2395093394
5 changed files with 49 additions and 75 deletions

View File

@@ -7,22 +7,20 @@ from typing import Optional
from fastapi import Request
from src.config import config
def get_client_ip(request: Request) -> str:
"""
获取客户端真实IP地址
按优先级检查:
1. X-Forwarded-For 头(支持代理链,根据可信代理数量提取
2. X-Real-IP 头Nginx 代理
1. X-Real-IP 头(最可靠,由最外层可信 Nginx 直接设置
2. X-Forwarded-For 头的第一个 IP原始客户端
3. 直接客户端IP
安全说明:
- 此函数根据 TRUSTED_PROXY_COUNT 配置来决定信任的代理层数
- 当 TRUSTED_PROXY_COUNT=0 时,不信任任何代理头,直接使用连接 IP
- 当服务直接暴露公网时,应设置 TRUSTED_PROXY_COUNT=0 以防止 IP 伪造
- X-Real-IP 优先级最高,因为它通常由最外层 Nginx 设置为 $remote_addr
Nginx 会直接覆盖这个头,不会传递客户端伪造的值
- 只要最外层 Nginx 配置了 proxy_set_header X-Real-IP $remote_addr; 即可正确获取真实 IP
Args:
request: FastAPI Request 对象
@@ -30,30 +28,19 @@ def get_client_ip(request: Request) -> str:
Returns:
str: 客户端IP地址如果无法获取则返回 "unknown"
"""
trusted_proxy_count = config.trusted_proxy_count
# 如果不信任任何代理,直接返回连接 IP
if trusted_proxy_count == 0:
if request.client and request.client.host:
return request.client.host
return "unknown"
# 优先检查 X-Forwarded-For 头(可能包含代理链)
forwarded_for = request.headers.get("X-Forwarded-For")
if forwarded_for:
# X-Forwarded-For 格式: "client, proxy1, proxy2"
# 从右往左数 trusted_proxy_count 个,取其左边的第一个
ips = [ip.strip() for ip in forwarded_for.split(",") if ip.strip()]
if len(ips) > trusted_proxy_count:
return ips[-(trusted_proxy_count + 1)]
elif ips:
return ips[0]
# 检查 X-Real-IP 头(通常由 Nginx 设置)
# 优先检查 X-Real-IP 头(由最外层 Nginx 设置,最可靠)
real_ip = request.headers.get("X-Real-IP")
if real_ip:
return real_ip.strip()
# 检查 X-Forwarded-For 头,取第一个 IP原始客户端
forwarded_for = request.headers.get("X-Forwarded-For")
if forwarded_for:
# X-Forwarded-For 格式: "client, proxy1, proxy2"
ips = [ip.strip() for ip in forwarded_for.split(",") if ip.strip()]
if ips:
return ips[0]
# 回退到直接客户端IP
if request.client and request.client.host:
return request.client.host
@@ -109,36 +96,26 @@ def get_request_metadata(request: Request) -> dict:
}
def extract_ip_from_headers(headers: dict, trusted_proxy_count: Optional[int] = None) -> str:
def extract_ip_from_headers(headers: dict) -> str:
"""
从HTTP头字典中提取IP地址用于中间件等场景
Args:
headers: HTTP头字典
trusted_proxy_count: 可信代理层数None 时使用配置值
Returns:
str: 客户端IP地址
"""
if trusted_proxy_count is None:
trusted_proxy_count = config.trusted_proxy_count
# 如果不信任任何代理,返回 unknown调用方需要用其他方式获取连接 IP
if trusted_proxy_count == 0:
return "unknown"
# 检查 X-Forwarded-For
forwarded_for = headers.get("x-forwarded-for", "")
if forwarded_for:
ips = [ip.strip() for ip in forwarded_for.split(",") if ip.strip()]
if len(ips) > trusted_proxy_count:
return ips[-(trusted_proxy_count + 1)]
elif ips:
return ips[0]
# 检查 X-Real-IP
# 优先检查 X-Real-IP由最外层 Nginx 设置,最可靠)
real_ip = headers.get("x-real-ip", "")
if real_ip:
return real_ip.strip()
# 检查 X-Forwarded-For取第一个 IP
forwarded_for = headers.get("x-forwarded-for", "")
if forwarded_for:
ips = [ip.strip() for ip in forwarded_for.split(",") if ip.strip()]
if ips:
return ips[0]
return "unknown"