如何在Django与Django Rest Framework开发的HTTP API中添加IP白名单?
Django + DRF 接口IP白名单限制实现方案
以下是两种常用实现方式,可根据实际需求选择:
方案一:自定义Django中间件(全局生效,适合所有接口都需校验的场景)
这种方式在请求进入视图层之前就完成IP校验,性能损耗最低,所有HTTP请求都会触发校验。
实现步骤:
- 在
settings.py中添加白名单配置,支持单个IP和CIDR网段:
# settings.py ALLOWED_IP_WHITELIST = [ "127.0.0.1", "192.168.1.0/24", # 按需添加你的IP ]
- 在你的APP目录下新建
ip_middleware.py,写入以下逻辑:
# ip_middleware.py from ipaddress import ip_address, ip_network from django.conf import settings from django.http import HttpResponseForbidden class IPWhitelistMiddleware: def __init__(self, get_response): self.get_response = get_response # 提前把网段转换成ip_network对象,避免每次请求都重复转换提升性能 self.allowed_ips = [] self.allowed_networks = [] for item in settings.ALLOWED_IP_WHITELIST: if "/" in item: self.allowed_networks.append(ip_network(item, strict=False)) else: self.allowed_ips.append(ip_address(item)) def __call__(self, request): # 获取客户端真实IP,有反向代理的情况取X-Forwarded-For头 x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR') if x_forwarded_for: client_ip = ip_address(x_forwarded_for.split(',')[0].strip()) else: client_ip = ip_address(request.META.get('REMOTE_ADDR')) # 校验IP是否在白名单 ip_allowed = client_ip in self.allowed_ips if not ip_allowed: for network in self.allowed_networks: if client_ip in network: ip_allowed = True break if not ip_allowed: return HttpResponseForbidden("Access denied") response = self.get_response(request) return response
- 在
settings.py的MIDDLEWARE列表中添加该中间件,放在列表靠前的位置,优先执行校验:
# settings.py MIDDLEWARE = [ "django.middleware.security.SecurityMiddleware", # 新增的IP校验中间件,放在安全中间件之后即可 "你的APP名称.ip_middleware.IPWhitelistMiddleware", # 其他原有中间件 ... ]
方案二:DRF自定义权限类(仅对DRF接口生效,可灵活控制作用范围)
如果只需要限制DRF的API接口,或者只需要给部分接口加IP限制,用这种方案更灵活。
实现步骤:
- 白名单配置和上面一致,先加在
settings.py中 - 在APP目录下新建
permissions.py,写入自定义权限类:
# permissions.py from ipaddress import ip_address, ip_network from django.conf import settings from rest_framework.permissions import BasePermission class IPWhitelistPermission(BasePermission): message = "Access denied" # 提前转换白名单格式提升性能 allowed_ips = [] allowed_networks = [] for item in settings.ALLOWED_IP_WHITELIST: if "/" in item: allowed_networks.append(ip_network(item, strict=False)) else: allowed_ips.append(ip_address(item)) def has_permission(self, request, view): x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR') if x_forwarded_for: client_ip = ip_address(x_forwarded_for.split(',')[0].strip()) else: client_ip = ip_address(request.META.get('REMOTE_ADDR')) if client_ip in self.allowed_ips: return True for network in self.allowed_networks: if client_ip in network: return True return False
- 启用方式二选一:
- 全局生效:所有DRF接口都开启IP校验,在
settings.py中添加配置
# settings.py REST_FRAMEWORK = { 'DEFAULT_PERMISSION_CLASSES': [ "你的APP名称.permissions.IPWhitelistPermission", # 其他原有权限类 ... ] }
- 单个视图生效:只给指定接口加IP校验,在对应的视图类中添加配置
from rest_framework.views import APIView from 你的APP名称.permissions import IPWhitelistPermission class YourAPIView(APIView): permission_classes = [IPWhitelistPermission] # 后续视图逻辑 ...
注意事项
- 如果你使用了Nginx、CDN等反向代理服务,必须配置代理服务传递真实客户端IP,以Nginx为例,需要在配置中添加
proxy_set_header X-Forwarded-For $remote_addr;,否则会获取到代理服务器的IP导致校验失效 - 生产环境建议将白名单配置在环境变量或数据库中,避免修改白名单需要重启服务
- 拦截请求返回的错误信息不要暴露IP校验的细节,降低被攻击的风险
内容的提问来源于stack exchange,提问作者Pranav Pateriya
相关产品推荐
相关产品推荐

