如何检测Django API请求来自本站还是外部客户端
Django+DRF 差异化限流实现方案
前置问题修复:请求元数据异常
REMOTE_ADDR为空通常是服务前置了Nginx、CDN等反向代理,Django未配置代理信任规则导致无法获取真实客户端IP,并非字段本身不可用,先做如下配置修复:
- 在
settings.py中配置SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')、USE_X_FORWARDED_HOST = True,同时将反向代理所属网段加入INTERNAL_IPS列表,Django会自动从代理传入的X-Forwarded-For头中提取真实客户端IP,填充到REMOTE_ADDR字段。 - 注意:仅配置自身可控的可信代理网段,不要信任任意来源传入的
X-Forwarded-For头,否则会存在IP伪造、限流被绕过的风险。
客户端身份可靠识别方案
HTTP_REFERER、User-Agent这类字段可被随意伪造、也可能被浏览器/代理层拦截,完全不适合作为客户端身份判断依据。针对同根域名下的Angular客户端,推荐以下两种可防伪造的识别方案,可根据安全等级需求选其一或组合使用:
方案1:带签名的自定义请求头(实现成本最低)
在Angular客户端全局HTTP拦截器中为所有发往后端的请求添加自定义标识头,同时增加签名校验避免头被伪造:
- 前后端部署时共享一个仅内部可见的签名密钥,Angular端发起请求时,生成当前时间戳,将时间戳拼接密钥做哈希运算,分别将
X-Client-Type: angular-web、X-Client-Ts: <当前时间戳>、X-Client-Sign: <哈希值>三个头加入请求。 - Django层添加轻量中间件,校验三个规则:时间戳与服务器当前时间差不超过300秒、签名哈希匹配、客户端类型头值正确,全部满足则判定为来自自有Angular客户端的合法请求。
- 跨域配置中,将上述自定义头加入
CORS_ALLOW_HEADERS列表,允许Angular所属域名的请求携带这些头。
方案2:根域HttpOnly Cookie校验(防伪造能力更强)
由于Angular客户端与后端同属*.mydomain.com根域,可利用Cookie的同域特性做身份校验:
- 后端提供一个客户端初始化接口,Angular应用首次加载时先调用该接口,后端在响应中设置根域级HttpOnly Cookie:
Set-Cookie: client_verify_token=<随机生成的校验串>; Domain=.mydomain.com; HttpOnly; Secure; SameSite=Strict,校验串存入服务端缓存,有效期与前端会话时长对齐。 - 后续同域请求会自动携带该Cookie,后端校验缓存中存在对应有效token则判定为自有Angular客户端请求。
- 该方案的优势是HttpOnly Cookie无法被前端JS读取,外部请求无法直接获取有效校验串,伪造门槛远高于自定义头方案,且不需要前端实现签名逻辑。
DRF自定义限流实现
完成身份识别逻辑后,继承DRF自带的SimpleRateThrottle实现两类限流规则即可:
from rest_framework.throttling import SimpleRateThrottle from myapp.utils import is_legitimate_angular_request # 导入前面实现的客户端校验逻辑 class AngularClientThrottle(SimpleRateThrottle): scope = "angular_client" def get_cache_key(self, request, view): if is_legitimate_angular_request(request): ident = self.get_ident(request) return self.cache_format % {"scope": self.scope, "ident": ident} return None class ExternalRequestThrottle(SimpleRateThrottle): scope = "external_request" def get_cache_key(self, request, view): if not is_legitimate_angular_request(request): ident = self.get_ident(request) return self.cache_format % {"scope": self.scope, "ident": ident} return None
在settings.py中注册限流类并配置对应阈值:
REST_FRAMEWORK = { "DEFAULT_THROTTLE_CLASSES": [ "myapp.throttles.AngularClientThrottle", "myapp.throttles.ExternalRequestThrottle", ], "DEFAULT_THROTTLE_RATES": { "angular_client": "1000/min", # 自有Angular客户端配置更高限流阈值 "external_request": "30/min", # curl等外部请求配置更严格的阈值 } }
避坑说明
- 所有用于客户端识别的标识字段,必须做服务端签名/有效性校验,禁止仅凭明文头、Cookie值判断身份,否则任何人都可以通过curl添加相同头直接绕过限流规则。
- 如果服务链路中包含CDN,需要在CDN配置中透传自定义的校验头,否则请求到达Django层时头会被CDN过滤,导致识别逻辑失效。
- 禁止使用
User-Agent作为判断依据,该字段可被任意客户端随意修改,curl通过-A参数即可模拟任意浏览器UA,无任何校验价值。
内容的提问来源于stack exchange,提问作者gmcc051
相关产品推荐
相关产品推荐

